diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-20 15:06:40 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-22 11:48:58 +0000 |
commit | daa093eea7c773db06799a13bd7e4e2e2a9f8f14 (patch) | |
tree | 96cc5e7b9194c1b29eab927730bfa419e7111c25 /chromium/chrome/browser/resources | |
parent | be59a35641616a4cf23c4a13fa0632624b021c1b (diff) | |
download | qtwebengine-chromium-daa093eea7c773db06799a13bd7e4e2e2a9f8f14.tar.gz |
BASELINE: Update Chromium to 63.0.3239.58
Change-Id: Ia93b322a00ba4dd4004f3bcf1254063ba90e1605
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/chrome/browser/resources')
439 files changed, 9669 insertions, 13072 deletions
diff --git a/chromium/chrome/browser/resources/BUILD.gn b/chromium/chrome/browser/resources/BUILD.gn index 39e356bfb69..b2d10f2a0ba 100644 --- a/chromium/chrome/browser/resources/BUILD.gn +++ b/chromium/chrome/browser/resources/BUILD.gn @@ -95,7 +95,7 @@ if (!is_android) { } grit("settings_resources") { - if (use_vulcanize) { + if (optimize_webui) { source = "settings/settings_resources_vulcanized.grd" # The .grd contains references to generated files. @@ -124,6 +124,34 @@ if (!is_android) { } if (enable_extensions) { + grit("extensions_resources") { + if (optimize_webui) { + source = "md_extensions/extensions_resources_vulcanized.grd" + + # The .grd contains references to generated files. + source_is_generated = true + + deps = [ + "//chrome/browser/resources/md_extensions:build", + ] + grit_flags = [ + "-E", + "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir), + ] + } else { + source = "md_extensions/extensions_resources.grd" + } + + defines = chrome_grit_defines + outputs = [ + "grit/extensions_resources.h", + "grit/extensions_resources_map.cc", + "grit/extensions_resources_map.h", + "extensions_resources.pak", + ] + output_dir = "$root_gen_dir/chrome" + } + grit("sync_file_system_internals_resources") { source = "sync_file_system_internals_resources.grd" defines = chrome_grit_defines diff --git a/chromium/chrome/browser/resources/PRESUBMIT.py b/chromium/chrome/browser/resources/PRESUBMIT.py index b8a8396d241..e7a3e430986 100644 --- a/chromium/chrome/browser/resources/PRESUBMIT.py +++ b/chromium/chrome/browser/resources/PRESUBMIT.py @@ -96,9 +96,9 @@ def CheckHtml(input_api, output_api): input_api, output_api, 80, lambda x: x.LocalPath().endswith('.html')) -def RunVulcanizeTests(input_api, output_api): +def RunOptimizeWebUiTests(input_api, output_api): presubmit_path = input_api.PresubmitLocalPath() - tests = [input_api.os_path.join(presubmit_path, 'vulcanize_gn_test.py')] + tests = [input_api.os_path.join(presubmit_path, 'optimize_webui_test.py')] return input_api.canned_checks.RunUnitTests(input_api, output_api, tests) @@ -123,8 +123,8 @@ def _CheckChangeOnUploadOrCommit(input_api, output_api): affected = input_api.AffectedFiles() if any(f for f in affected if f.LocalPath().endswith('.html')): results += CheckHtml(input_api, output_api) - if any(f for f in affected if f.LocalPath().endswith('vulcanize_gn.py')): - results += RunVulcanizeTests(input_api, output_api) + if any(f for f in affected if f.LocalPath().endswith('optimize_webui.py')): + results += RunOptimizeWebUiTests(input_api, output_api) results += _CheckWebDevStyle(input_api, output_api) results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api, check_js=True) diff --git a/chromium/chrome/browser/resources/bluetooth_internals/adapter_broker.js b/chromium/chrome/browser/resources/bluetooth_internals/adapter_broker.js index 37c8fb829a4..df04fc91b8f 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/adapter_broker.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/adapter_broker.js @@ -7,11 +7,11 @@ * chrome://bluetooth-internals/. */ cr.define('adapter_broker', function() { - /** @typedef {interfaces.BluetoothAdapter.Adapter.ptrClass} */ + /** @typedef {bluetooth.mojom.Adapter.ptrClass} */ var AdapterPtr; - /** @typedef {interfaces.BluetoothDevice.Device.ptrClass} */ + /** @typedef {bluetooth.mojom.Device.ptrClass} */ var DevicePtr; - /** @typedef {interfaces.BluetoothAdapter.DiscoverySession.ptrClass} */ + /** @typedef {bluetooth.mojom.DiscoverySession.ptrClass} */ var DiscoverySessionPtr; /** @@ -51,11 +51,10 @@ cr.define('adapter_broker', function() { */ connectToDevice: function(address) { return this.adapter_.connectToDevice(address).then(function(response) { - if (response.result != - interfaces.BluetoothAdapter.ConnectResult.SUCCESS) { + if (response.result != bluetooth.mojom.ConnectResult.SUCCESS) { // TODO(crbug.com/663394): Replace with more descriptive error // messages. - var ConnectResult = interfaces.BluetoothAdapter.ConnectResult; + var ConnectResult = bluetooth.mojom.ConnectResult; var errorString = Object.keys(ConnectResult).find(function(key) { return ConnectResult[key] === response.result; }); @@ -69,7 +68,7 @@ cr.define('adapter_broker', function() { /** * Gets an array of currently detectable devices from the Adapter service. - * @return {!Array<!interfaces.BluetoothDevice.DeviceInfo>} + * @return {!Array<!bluetooth.mojom.DeviceInfo>} */ getDevices: function() { return this.adapter_.getDevices(); @@ -77,7 +76,7 @@ cr.define('adapter_broker', function() { /** * Gets the current state of the Adapter. - * @return {!interfaces.BluetoothAdapter.AdapterInfo} + * @return {!bluetooth.mojom.AdapterInfo} */ getInfo: function() { return this.adapter_.getInfo(); @@ -85,11 +84,11 @@ cr.define('adapter_broker', function() { /** * Sets client of Adapter service. - * @param {!interfaces.BluetoothAdapter.AdapterClient} adapterClient + * @param {!bluetooth.mojom.AdapterClient} adapterClient */ setClient: function(adapterClient) { - adapterClient.binding = new interfaces.Bindings.Binding( - interfaces.BluetoothAdapter.AdapterClient, adapterClient); + adapterClient.binding = + new mojo.Binding(bluetooth.mojom.AdapterClient, adapterClient); this.adapter_.setClient( adapterClient.binding.createInterfacePtrAndBind()); @@ -181,7 +180,7 @@ cr.define('adapter_broker', function() { /** * Fires deviceadded event. - * @param {!interfaces.BluetoothDevice.DeviceInfo} deviceInfo + * @param {!bluetooth.mojom.DeviceInfo} deviceInfo */ deviceAdded: function(deviceInfo) { var event = @@ -191,7 +190,7 @@ cr.define('adapter_broker', function() { /** * Fires devicechanged event. - * @param {!interfaces.BluetoothDevice.DeviceInfo} deviceInfo + * @param {!bluetooth.mojom.DeviceInfo} deviceInfo */ deviceChanged: function(deviceInfo) { var event = @@ -201,7 +200,7 @@ cr.define('adapter_broker', function() { /** * Fires deviceremoved event. - * @param {!interfaces.BluetoothDevice.DeviceInfo} deviceInfo + * @param {!bluetooth.mojom.DeviceInfo} deviceInfo */ deviceRemoved: function(deviceInfo) { var event = @@ -221,24 +220,20 @@ cr.define('adapter_broker', function() { if (adapterBroker) return Promise.resolve(adapterBroker); - return interfaces.setupInterfaces() - .then(function(adapter) { - var adapterFactory = - new interfaces.BluetoothAdapter.AdapterFactoryPtr( - interfaces.FrameInterfaces.getInterface( - interfaces.BluetoothAdapter.AdapterFactory.name)); - - // Get an Adapter service. - return adapterFactory.getAdapter(); - }) - .then(function(response) { - if (!response.adapter.ptr.isBound()) { - throw new Error('Bluetooth Not Supported on this platform.'); - } - - adapterBroker = new AdapterBroker(response.adapter); - return adapterBroker; - }); + var adapterFactory = new bluetooth.mojom.AdapterFactoryPtr; + Mojo.bindInterface( + bluetooth.mojom.AdapterFactory.name, + mojo.makeRequest(adapterFactory).handle); + + // Get an Adapter service. + return adapterFactory.getAdapter().then(function(response) { + if (!response.adapter.ptr.isBound()) { + throw new Error('Bluetooth Not Supported on this platform.'); + } + + adapterBroker = new AdapterBroker(response.adapter); + return adapterBroker; + }); } return { diff --git a/chromium/chrome/browser/resources/bluetooth_internals/adapter_page.js b/chromium/chrome/browser/resources/bluetooth_internals/adapter_page.js index 98d296a192f..b032be08852 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/adapter_page.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/adapter_page.js @@ -43,7 +43,7 @@ cr.define('adapter_page', function() { /** * Sets the information to display in fieldset. - * @param {!interfaces.BluetoothAdapter.AdapterInfo} info + * @param {!bluetooth.mojom.AdapterInfo} info */ setAdapterInfo: function(info) { this.adapterFieldSet.setObject(info); diff --git a/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html b/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html index 8ad7e7c7bed..40f1bce873c 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html +++ b/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html @@ -24,8 +24,11 @@ <link rel="import" href="chrome://resources/html/cr/ui/page_manager/page.html"> <link rel="import" href="chrome://resources/html/util.html"> + <script src="chrome://resources/js/mojo_bindings.js"></script> + <script src="uuid.mojom.js"></script> + <script src="device.mojom.js"></script> + <script src="adapter.mojom.js"></script> <script src="snackbar.js"></script> - <script src="interfaces.js"></script> <script src="adapter_broker.js"></script> <script src="device_broker.js"></script> <script src="object_fieldset.js"></script> @@ -85,11 +88,11 @@ <table> <thead> <tr> - <th data-field="name_for_display">Name</th> + <th data-field="nameForDisplay">Name</th> <th data-field="address">Address</th> <th data-field="rssi.value">Latest RSSI</th> <th data-field="services.length">Services</th> - <th data-field="is_gatt_connected">GATT Connection State</th> + <th data-field="isGattConnected">GATT Connection State</th> <th></th> </tr> </thead> @@ -111,4 +114,4 @@ </div> </template> -</html>
\ No newline at end of file +</html> diff --git a/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js b/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js index 6eedc5a88cc..ff82b194ed6 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.js @@ -30,7 +30,7 @@ cr.define('bluetooth_internals', function() { /** @type {devices_page.DevicesPage} */ var devicesPage = null; - /** @type {interfaces.BluetoothAdapter.DiscoverySession.ptrClass} */ + /** @type {bluetooth.mojom.DiscoverySession.ptrClass} */ var discoverySession = null; /** @type {boolean} */ @@ -87,7 +87,7 @@ cr.define('bluetooth_internals', function() { * '#page-container', and adds a sidebar item to show the new page. If a * page exists that matches |deviceInfo.address|, nothing is created and the * existing page is returned. - * @param {!interfaces.BluetoothDevice.Device} deviceInfo + * @param {!bluetooth.mojom.Device} deviceInfo * @return {!device_details_page.DeviceDetailsPage} */ function makeDeviceDetailsPage(deviceInfo) { @@ -125,7 +125,7 @@ cr.define('bluetooth_internals', function() { sidebarObj.addItem({ pageName: deviceDetailsPageId, - text: deviceInfo.name_for_display, + text: deviceInfo.nameForDisplay, }); deviceDetailsPage.connect(); @@ -275,9 +275,13 @@ cr.define('bluetooth_internals', function() { } function initializeViews() { - setupPages(); - - adapter_broker.getAdapterBroker() + // window.setupFn() provides a hook for the test suite to perform setup + // actions after the page is loaded but before any script is run. + window.setupFn() + .then(function() { + setupPages(); + return adapter_broker.getAdapterBroker(); + }) .then(function(broker) { adapterBroker = broker; }) @@ -300,5 +304,9 @@ cr.define('bluetooth_internals', function() { }; }); +window.setupFn = window.setupFn || function() { + return Promise.resolve(); +}; + document.addEventListener( 'DOMContentLoaded', bluetooth_internals.initializeViews); diff --git a/chromium/chrome/browser/resources/bluetooth_internals/characteristic_list.js b/chromium/chrome/browser/resources/bluetooth_internals/characteristic_list.js index 71d93b8c69c..ecee19eb0fe 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/characteristic_list.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/characteristic_list.js @@ -44,7 +44,7 @@ cr.define('characteristic_list', function() { * properties, 'id' and 'uuid', and one for the 'properties' bitfield in the * CharacteristicInfo object. * @constructor - * @param {!interfaces.BluetoothDevice.CharacteristicInfo} characteristicInfo + * @param {!bluetooth.mojom.CharacteristicInfo} characteristicInfo * @param {string} deviceAddress * @param {string} serviceId */ @@ -53,7 +53,7 @@ cr.define('characteristic_list', function() { var listItem = new ExpandableListItem(); listItem.__proto__ = CharacteristicListItem.prototype; - /** @type {!interfaces.BluetoothDevice.CharacteristicInfo} */ + /** @type {!bluetooth.mojom.CharacteristicInfo} */ listItem.info = characteristicInfo; /** @private {string} */ listItem.deviceAddress_ = deviceAddress; @@ -87,7 +87,7 @@ cr.define('characteristic_list', function() { this.propertiesFieldSet_ = new object_fieldset.ObjectFieldSet(); this.propertiesFieldSet_.setPropertyDisplayNames( PROPERTIES_PROPERTY_NAMES); - var Property = interfaces.BluetoothDevice.Property; + var Property = bluetooth.mojom.Property; this.propertiesFieldSet_.setObject({ broadcast: (this.info.properties & Property.BROADCAST) > 0, read: (this.info.properties & Property.READ) > 0, @@ -120,7 +120,7 @@ cr.define('characteristic_list', function() { characteristicId: this.info.id, properties: this.info.properties, }); - this.valueControl_.setValue(this.info.last_known_value); + this.valueControl_.setValue(this.info.lastKnownValue); /** @private {!descriptor_list.DescriptorList} */ this.descriptorList_ = new descriptor_list.DescriptorList(); diff --git a/chromium/chrome/browser/resources/bluetooth_internals/descriptor_list.js b/chromium/chrome/browser/resources/bluetooth_internals/descriptor_list.js index 13d646b1147..f68e29069bf 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/descriptor_list.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/descriptor_list.js @@ -25,7 +25,7 @@ cr.define('descriptor_list', function() { * A fieldset is created within the element for the primitive * properties, 'id' and 'uuid' within the DescriptorInfo object. * @constructor - * @param {!interfaces.BluetoothDevice.DescriptorInfo} descriptorInfo + * @param {!bluetooth.mojom.DescriptorInfo} descriptorInfo * @param {string} deviceAddress * @param {string} serviceId * @param {string} characteristicId @@ -35,7 +35,7 @@ cr.define('descriptor_list', function() { var listItem = new ExpandableListItem(); listItem.__proto__ = DescriptorListItem.prototype; - /** @type {!interfaces.BluetoothDevice.DescriptorInfo} */ + /** @type {!bluetooth.mojom.DescriptorInfo} */ listItem.info = descriptorInfo; /** @private {string} */ listItem.deviceAddress_ = deviceAddress; @@ -184,4 +184,4 @@ cr.define('descriptor_list', function() { DescriptorList: DescriptorList, DescriptorListItem: DescriptorListItem, }; -});
\ No newline at end of file +}); diff --git a/chromium/chrome/browser/resources/bluetooth_internals/device_broker.js b/chromium/chrome/browser/resources/bluetooth_internals/device_broker.js index 5704e5a1a7e..acb551c0662 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/device_broker.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/device_broker.js @@ -10,7 +10,7 @@ */ // Expose for testing. -/** @type {!Map<string, !interfaces.BluetoothDevice.DevicePtr|!Promise} */ +/** @type {!Map<string, !bluetooth.mojom.DevicePtr|!Promise} */ var connectedDevices = null; cr.define('device_broker', function() { @@ -22,7 +22,7 @@ cr.define('device_broker', function() { * DevicePtr. If a connection is in progress, the promise resolves when * the existing connection request promise is fulfilled. * @param {string} address - * @return {!Promise<!interfaces.BluetoothDevice.DevicePtr>} + * @return {!Promise<!bluetooth.mojom.DevicePtr>} */ function connectToDevice(address) { var deviceOrPromise = connectedDevices.get(address) || null; diff --git a/chromium/chrome/browser/resources/bluetooth_internals/device_collection.js b/chromium/chrome/browser/resources/bluetooth_internals/device_collection.js index 7245f26c2ef..ffec1c491fa 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/device_collection.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/device_collection.js @@ -50,7 +50,7 @@ cr.define('device_collection', function() { /** * Adds or updates a Device with new DeviceInfo. - * @param {!interfaces.BluetoothDevice.DeviceInfo} deviceInfo + * @param {!bluetooth.mojom.DeviceInfo} deviceInfo */ addOrUpdate: function(deviceInfo) { deviceInfo.removed = false; @@ -75,7 +75,7 @@ cr.define('device_collection', function() { /** * Marks the Device as removed. - * @param {!interfaces.bluetoothDevice.DeviceInfo} deviceInfo + * @param {!bluetooth.mojom.DeviceInfo} deviceInfo */ remove: function(deviceInfo) { var device = this.getByAddress(deviceInfo.address); diff --git a/chromium/chrome/browser/resources/bluetooth_internals/device_details_page.js b/chromium/chrome/browser/resources/bluetooth_internals/device_details_page.js index 7a573910768..626b18a5bda 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/device_details_page.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/device_details_page.js @@ -20,7 +20,7 @@ cr.define('device_details_page', function() { var PROPERTY_NAMES = { name: 'Name', address: 'Address', - is_gatt_connected: 'GATT Connected', + isGattConnected: 'GATT Connected', 'rssi.value': 'Latest RSSI', 'services.length': 'Services', }; @@ -33,16 +33,16 @@ cr.define('device_details_page', function() { * compononent that lists all of the active services on the device. * @constructor * @param {string} id - * @param {!interfaces.BluetoothDevice.DeviceInfo} deviceInfo + * @param {!bluetooth.mojom.DeviceInfo} deviceInfo * @extends {cr.ui.pageManager.Page} */ function DeviceDetailsPage(id, deviceInfo) { - Page.call(this, id, deviceInfo.name_for_display, id); + Page.call(this, id, deviceInfo.nameForDisplay, id); - /** @type {interfaces.BluetoothDevice.DeviceInfo} */ + /** @type !bluetooth.mojom.DeviceInfo} */ this.deviceInfo = deviceInfo; - /** @private {?interfaces.BluetoothDevice.Device.ptrClass} */ + /** @private {!bluetooth.mojom.Device.ptrClass} */ this.devicePtr_ = null; /** @private {!object_fieldset.ObjectFieldSet} */ @@ -118,7 +118,7 @@ cr.define('device_details_page', function() { } Snackbar.show( - this.deviceInfo.name_for_display + ': ' + error.message, + this.deviceInfo.nameForDisplay + ': ' + error.message, SnackbarType.ERROR, 'Retry', this.connect.bind(this)); this.updateConnectionStatus_( @@ -139,7 +139,7 @@ cr.define('device_details_page', function() { /** Redraws the contents of the page with the current |deviceInfo|. */ redraw: function() { - var isConnected = this.deviceInfo.is_gatt_connected; + var isConnected = this.deviceInfo.isGattConnected; // Update status if connection has changed. if (isConnected) @@ -161,9 +161,9 @@ cr.define('device_details_page', function() { serviceCount = services.length; var deviceViewObj = { - name: this.deviceInfo.name_for_display, + name: this.deviceInfo.nameForDisplay, address: this.deviceInfo.address, - is_gatt_connected: connectedText, + isGattConnected: connectedText, 'rssi.value': rssiValue, 'services.length': serviceCount, }; @@ -174,7 +174,7 @@ cr.define('device_details_page', function() { /** * Sets the page's device info and forces a redraw. - * @param {!interfaces.BluetoothDevice.DeviceInfo} + * @param {!bluetooth.mojom.DeviceInfo} */ setDeviceInfo: function(info) { this.deviceInfo = info; diff --git a/chromium/chrome/browser/resources/bluetooth_internals/device_table.js b/chromium/chrome/browser/resources/bluetooth_internals/device_table.js index 0068e27d123..6fdc7b686f4 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/device_table.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/device_table.js @@ -43,7 +43,7 @@ cr.define('device_table', function() { this.body_ = this.tBodies[0]; /** @private */ this.headers_ = this.tHead.rows[0].cells; - /** @private {!Map<!interfaces.BluetoothDevice.DeviceInfo, boolean>} */ + /** @private {!Map<!bluetooth.mojom.DeviceInfo, boolean>} */ this.inspectionMap_ = new Map(); }, @@ -66,7 +66,7 @@ cr.define('device_table', function() { * Updates the inspect status of the row matching the given |deviceInfo|. * If |isInspecting| is true, the forget link is enabled otherwise it's * disabled. - * @param {!interfaces.BluetoothDevice.DeviceInfo} deviceInfo + * @param {!bluetooth.mojom.DeviceInfo} deviceInfo * @param {boolean} isInspecting */ setInspecting: function(deviceInfo, isInspecting) { @@ -130,7 +130,7 @@ cr.define('device_table', function() { /** * Inserts a new row at |index| and updates it with info from |device|. - * @param {!interfaces.BluetoothDevice.DeviceInfo} device + * @param {!bluetooth.mojom.DeviceInfo} device * @param {?number} index * @private */ @@ -182,7 +182,7 @@ cr.define('device_table', function() { /** * Updates the row at |index| with the info from |device|. - * @param {!interfaces.BluetoothDevice.DeviceInfo} device + * @param {!bluetooth.mojom.DeviceInfo} device * @param {number} index * @private */ @@ -215,7 +215,7 @@ cr.define('device_table', function() { obj = obj[part]; } - if (propName == 'is_gatt_connected') { + if (propName == 'isGattConnected') { obj = obj ? 'Connected' : 'Not Connected'; } diff --git a/chromium/chrome/browser/resources/bluetooth_internals/devices_page.js b/chromium/chrome/browser/resources/bluetooth_internals/devices_page.js index 39c72a778a8..3c62fd70e5a 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/devices_page.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/devices_page.js @@ -51,7 +51,7 @@ cr.define('devices_page', function() { /** * Updates the inspect status of the given |deviceInfo| in the device table. - * @param {!interfaces.BluetoothDevice.DeviceInfo} deviceInfo + * @param {!bluetooth.mojom.DeviceInfo} deviceInfo * @param {boolean} isInspecting */ setInspecting: function(deviceInfo, isInspecting) { diff --git a/chromium/chrome/browser/resources/bluetooth_internals/interfaces.js b/chromium/chrome/browser/resources/bluetooth_internals/interfaces.js deleted file mode 100644 index 5bec864ca04..00000000000 --- a/chromium/chrome/browser/resources/bluetooth_internals/interfaces.js +++ /dev/null @@ -1,45 +0,0 @@ -// 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. - -/** - * Javascript for Mojo interface helpers, served from - * chrome://bluetooth-internals/. - */ - -cr.define('interfaces', function() { - /** - * Overriden by tests to give them a chance to setup a fake Mojo browser proxy - * before any other code executes. - * @return {!Promise} A promise firing once necessary setup has been completed. - */ - var setupFn = window.setupFn || function() { - return Promise.resolve(); - }; - - /** - * Sets up Mojo interfaces and adds them to window.interfaces. - * @return {Promise} - */ - function setupInterfaces() { - return setupFn().then(function() { - return importModules([ - 'content/public/renderer/frame_interfaces', - 'device/bluetooth/public/interfaces/adapter.mojom', - 'device/bluetooth/public/interfaces/device.mojom', - 'mojo/public/js/bindings', - ]) - .then(function( - [frameInterfaces, bluetoothAdapter, bluetoothDevice, bindings]) { - interfaces.BluetoothAdapter = bluetoothAdapter; - interfaces.BluetoothDevice = bluetoothDevice; - interfaces.Bindings = bindings; - interfaces.FrameInterfaces = frameInterfaces; - }); - }); - } - - return { - setupInterfaces: setupInterfaces, - }; -}); diff --git a/chromium/chrome/browser/resources/bluetooth_internals/service_list.js b/chromium/chrome/browser/resources/bluetooth_internals/service_list.js index 60df11061dc..b58ec6d4cb6 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/service_list.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/service_list.js @@ -21,7 +21,7 @@ cr.define('service_list', function() { var PROPERTY_NAMES = { id: 'ID', 'uuid.uuid': 'UUID', - is_primary: 'Type', + isPrimary: 'Type', }; /** @@ -30,7 +30,7 @@ cr.define('service_list', function() { * contains an ObjectFieldSet that displays all of the properties in the * given |serviceInfo|. Data is not loaded until the ServiceListItem is * expanded for the first time. - * @param {!interfaces.BluetoothDevice.ServiceInfo} serviceInfo + * @param {!bluetooth.mojom.ServiceInfo} serviceInfo * @param {string} deviceAddress * @constructor */ @@ -38,7 +38,7 @@ cr.define('service_list', function() { var listItem = new ExpandableListItem(); listItem.__proto__ = ServiceListItem.prototype; - /** @type {!interfaces.BluetoothDevice.ServiceInfo} */ + /** @type {!bluetooth.mojom.ServiceInfo} */ listItem.info = serviceInfo; /** @private {string} */ listItem.deviceAddress_ = deviceAddress; @@ -64,7 +64,7 @@ cr.define('service_list', function() { this.serviceFieldSet_.setObject({ id: this.info.id, 'uuid.uuid': this.info.uuid.uuid, - is_primary: this.info.is_primary ? 'Primary' : 'Secondary', + isPrimary: this.info.isPrimary ? 'Primary' : 'Secondary', }); // Create content for display in brief content container. @@ -171,4 +171,4 @@ cr.define('service_list', function() { ServiceList: ServiceList, ServiceListItem: ServiceListItem, }; -});
\ No newline at end of file +}); diff --git a/chromium/chrome/browser/resources/bluetooth_internals/value_control.js b/chromium/chrome/browser/resources/bluetooth_internals/value_control.js index 04b5a4aabed..69dfc188cf8 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/value_control.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/value_control.js @@ -293,9 +293,9 @@ cr.define('value_control', function() { */ redraw: function() { this.readBtn_.hidden = - (this.properties_ & interfaces.BluetoothDevice.Property.READ) === 0; + (this.properties_ & bluetooth.mojom.Property.READ) === 0; this.writeBtn_.hidden = - (this.properties_ & interfaces.BluetoothDevice.Property.WRITE) === 0; + (this.properties_ & bluetooth.mojom.Property.WRITE) === 0; var isAvailable = !this.readBtn_.hidden || !this.writeBtn_.hidden; this.unavailableMessage_.hidden = isAvailable; @@ -319,13 +319,13 @@ cr.define('value_control', function() { /** * Gets an error string describing the given |result| code. - * @param {!interfaces.BluetoothDevice.GattResult} result + * @param {!bluetooth.mojom.GattResult} result * @private */ getErrorString_: function(result) { // TODO(crbug.com/663394): Replace with more descriptive error // messages. - var GattResult = interfaces.BluetoothDevice.GattResult; + var GattResult = bluetooth.mojom.GattResult; return Object.keys(GattResult).find(function(key) { return GattResult[key] === result; }); @@ -354,8 +354,7 @@ cr.define('value_control', function() { .then(function(response) { this.readBtn_.disabled = false; - if (response.result === - interfaces.BluetoothDevice.GattResult.SUCCESS) { + if (response.result === bluetooth.mojom.GattResult.SUCCESS) { this.setValue(response.value); Snackbar.show( this.deviceAddress_ + ': Read succeeded', @@ -395,8 +394,7 @@ cr.define('value_control', function() { .then(function(response) { this.writeBtn_.disabled = false; - if (response.result === - interfaces.BluetoothDevice.GattResult.SUCCESS) { + if (response.result === bluetooth.mojom.GattResult.SUCCESS) { Snackbar.show( this.deviceAddress_ + ': Write succeeded', SnackbarType.SUCCESS); @@ -415,4 +413,4 @@ cr.define('value_control', function() { ValueControl: ValueControl, ValueDataType: ValueDataType, }; -});
\ No newline at end of file +}); diff --git a/chromium/chrome/browser/resources/bookmark_manager/css/bmm.css b/chromium/chrome/browser/resources/bookmark_manager/css/bmm.css index adef2e44e21..a550ec56d1f 100644 --- a/chromium/chrome/browser/resources/bookmark_manager/css/bmm.css +++ b/chromium/chrome/browser/resources/bookmark_manager/css/bmm.css @@ -59,7 +59,7 @@ list .label-img-wrapper { z-index: -1; } -<if expr="not is_macosx and not is_ios"> +<if expr="not is_macosx"> html[dir=rtl] list > .folder .label-img-wrapper > * { transform: scaleX(-1); } diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/BUILD.gn b/chromium/chrome/browser/resources/chromeos/chromevox/BUILD.gn index 5f75f2aaf29..6e5018a8af0 100644 --- a/chromium/chrome/browser/resources/chromeos/chromevox/BUILD.gn +++ b/chromium/chrome/browser/resources/chromeos/chromevox/BUILD.gn @@ -4,6 +4,7 @@ import("//build/config/features.gni") import("//chrome/common/features.gni") +import("//components/nacl/features.gni") import("//testing/test.gni") import("//chrome/test/base/js2gtest.gni") import("run_jsbundler.gni") diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json b/chromium/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json index c08ebdc2bf8..76b4836f627 100644 --- a/chromium/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json +++ b/chromium/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json @@ -449,16 +449,6 @@ } }, { - "command": "toggleChromeVoxVersion", - "sequence": { - "cvoxModifier": true, - "keys": { - "keyCode": [81], - "shitKey": [true] - } - } - }, - { "command": "toggleStickyMode", "sequence": { "doubleTap": true, @@ -932,6 +922,24 @@ "keyCode": [119] } } + }, + { + "command": "enableChromeVoxArcSupportForCurrentApp", + "sequence": { + "cvoxModifier": true, + "keys": { + "keyCode": [65, 219] + } + } + }, + { + "command": "disableChromeVoxArcSupportForCurrentApp", + "sequence": { + "cvoxModifier": true, + "keys": { + "keyCode": [65, 221] + } + } } ] } diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd index 7a76f8bdf2d..dbbb3c6f00a 100644 --- a/chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd +++ b/chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd @@ -2533,22 +2533,12 @@ <message desc="Title of a notification that ChromeVox has been updated." name="IDS_CHROMEVOX_UPDATE_TITLE"> ChromeVox Updated </message> - <message desc="Message text for a notification that ChromeVox has been updated for a new experience." name="IDS_CHROMEVOX_UPDATE_MESSAGE_NEXT"> - Press ChromeVox o, n to learn more about ChromeVox Next. + <message desc="Message text for a notification that ChromeVox has been updated for a new release." name="IDS_CHROMEVOX_UPDATE_MESSAGE_NEW"> + Press ChromeVox o, n to find out what's new </message> <message desc="Options page description for the command to show ChromeVox's update page." name="IDS_CHROMEVOX_SHOW_NEXT_UPDATE_DESCRIPTION"> Show update notes </message> - <message desc="Checkbox label for toggling on/off ChromeVox's new beta experience." name="IDS_CHROMEVOX_OPTIONS_USE_NEXT_LABEL"> - Use ChromeVox Next (beta) - </message> - <message desc="Spoken when switching to the classic ChromeVox experience." name="IDS_CHROMEVOX_SWITCH_TO_CLASSIC"> - Switched to Classic ChromeVox - </message> - <message desc="Spoken when switching to the new beta experience." name="IDS_CHROMEVOX_SWITCH_TO_NEXT"> - Switched to ChromeVox Next - </message> - <message desc="Description of button that closes the ChromeVox Tutorial" name="IDS_CHROMEVOX_CLOSE_TUTORIAL"> Close ChromeVox Tutorial </message> @@ -2558,11 +2548,11 @@ <message desc="Button that goes to the next page in the ChromeVox Tutorial" name="IDS_CHROMEVOX_TUTORIAL_NEXT"> Next </message> - <message desc="Heading that welcomes users to the ChromeVox Next tutorial" name="IDS_CHROMEVOX_TUTORIAL_WELCOME_HEADING"> - Welcome to ChromeVox Next! + <message desc="Heading that welcomes users to the ChromeVox tutorial" name="IDS_CHROMEVOX_TUTORIAL_WELCOME_HEADING"> + Welcome to ChromeVox! </message> - <message desc="Introductory text for the 'ChromeVox Next' tutorial" name="IDS_CHROMEVOX_TUTORIAL_WELCOME_TEXT"> - Are you using ChromeVox Next spoken feedback for the first time? This quick tutorial explains the essentials for getting started with ChromeVox Next. + <message desc="Introductory text for the 'ChromeVox' tutorial" name="IDS_CHROMEVOX_TUTORIAL_WELCOME_TEXT"> + Are you using ChromeVox spoken feedback for the first time? This quick tutorial explains the essentials for getting started with ChromeVox. </message> <message desc="Text that tells users to press the enter key to move to the next page or backspace to move to the previous page in the tutorial" name="IDS_CHROMEVOX_TUTORIAL_ENTER_TO_ADVANCE"> To advance, press enter; to go back, press backspace. @@ -2580,7 +2570,7 @@ The ChromeVox modifier key </message> <message desc="Text explaining that the Search key on the keyboard will be held down for most ChromeVox shortcuts" name="IDS_CHROMEVOX_TUTORIAL_MODIFIER"> - In ChromeVox Next, the Search key is the modifier key. Most ChromeVox shortcuts start with the Search key. You’ll also use the arrow keys for navigation. + In ChromeVox, the Search key is the modifier key. Most ChromeVox shortcuts start with the Search key. You’ll also use the arrow keys for navigation. </message> <message desc="Text explaining where to find the Search key on a Chromebook keyboard" name="IDS_CHROMEVOX_TUTORIAL_CHROMEBOOK_SEARCH"> On the Chromebook, the Search key is immediately above the left Shift key. @@ -2634,29 +2624,29 @@ To open the Chrome browser menu, press Alt+F. Learn More </message> <message desc="Part of the ChromeVox tutorial, explaining that this is the end of the tutorial and that there are links to more information." name="IDS_CHROMEVOX_TUTORIAL_LEARN_MORE"> - Congratulations! You’ve learned the essentials to use ChromeVox Next (beta) successfully. Remember that you can open the ChromeVox command menu at any time by pressing Search+Period. To learn even more about ChromeVox and Chrome OS, visit the following articles. + Congratulations! You’ve learned the essentials to use ChromeVox successfully. Remember that you can open the ChromeVox command menu at any time by pressing Search+Period. To learn even more about ChromeVox and Chrome OS, visit the following articles. If you're done with the tutorial, use ChromeVox to navigate to the Close button and click it. </message> - <message desc="Part of the ChromeVox update notes for m56, title." name="IDS_CHROMEVOX_UPDATE_56_TITLE"> - You've been updated to a major new release of ChromeVox! + <message desc="Part of the ChromeVox update notes for m63, title." name="IDS_CHROMEVOX_UPDATE_63_TITLE"> + You've been updated to ChromeVox 63! </message> - <message desc="Part of the ChromeVox update notes for m56, introductory text." name="IDS_CHROMEVOX_UPDATE_56_INTRO"> - We've been hard at work on a new and improved ChromeVox experience; here's what's new + <message desc="Part of the ChromeVox update notes for m63, introductory text." name="IDS_CHROMEVOX_UPDATE_63_INTRO"> + We have some exciting changes in this new release; here's what's new: </message> - <message desc="Part of the ChromeVox update notes for m56, new items 1." name="IDS_CHROMEVOX_UPDATE_56_ITEM_1"> - New key bindings. We've made Search the ChromeVox modifier and dramatically simplified the key combinations. + <message desc="Part of the ChromeVox update notes for 63, new items 1." name="IDS_CHROMEVOX_UPDATE_63_ITEM_1"> + Classic removed. The keyboard toggle to switch back into ChromeVox Classic has been removed. </message> - <message desc="Part of the ChromeVox update notes for m56, new items 2." name="IDS_CHROMEVOX_UPDATE_56_ITEM_2"> - New earcons. We've refreshed the set of audio icons you hear to make browsing more responsive. + <message desc="Part of the ChromeVox update notes for m63, new items 2." name="IDS_CHROMEVOX_UPDATE_63_ITEM_2"> + Rich text support. ChromeVox now supports rich text fields including jump commands, braille cursor routing, selection, and more. </message> - <message desc="Part of the ChromeVox update notes for m56, new items 3." name="IDS_CHROMEVOX_UPDATE_56_ITEM_3"> - Under the hood upgrades. We've revamped how ChromeVox works from the bottom up. Expect better performance, stability, and web support/compliance. + <message desc="Part of the ChromeVox update notes for m63, new items 3." name="IDS_CHROMEVOX_UPDATE_63_ITEM_3"> + Android apps for Chrome. If you have Play Store on your device, get a sneak peek at ChromeVox with Android apps. Try the experimental support by downloading Google Chrome Canary from the Play Store. </message> - <message desc="Part of the ChromeVox update notes for m56, closing paragraph." name="IDS_CHROMEVOX_UPDATE_56_OUTTRO"> - We're excited to hear from you on this new experience. Send us feedback directly by pressing Search+a, then i. Press the next button to continue onto the tutorial to learn everything you need to get started. + <message desc="Part of the ChromeVox update notes for m63, closing paragraph." name="IDS_CHROMEVOX_UPDATE_63_OUTTRO"> + As always, you can send us feedback directly by pressing Search+a then i. We're eager to hear from you. </message> - <message desc="Title of an article on the command reference for 'ChromeVox Next'" name="IDS_CHROMEVOX_NEXT_COMMAND_REFERENCE"> - ChromeVox Next Command Reference + <message desc="Title of an article on the command reference for 'ChromeVox'" name="IDS_CHROMEVOX_NEXT_COMMAND_REFERENCE"> + ChromeVox Command Reference </message> <message desc="Title of an article on keyboard shortcuts for Chromebooks" name="IDS_CHROMEVOX_CHROME_KEYBOARD_SHORTCUTS"> Chromebook keyboard shortcuts @@ -2790,6 +2780,9 @@ If you're done with the tutorial, use ChromeVox to navigate to the Close button <message desc="Shown to a user when they press a braille keyboard command that requires on screen keyboard to be enabled." name="IDS_CHROMEVOX_ENABLE_VIRTUAL_KEYBOARD"> Please enable the on screen keyboard under status tray, accessibility to use extended braille commands. </message> + <message desc="Shown to a user when they invoke the read current title command in a context without a title." name="IDS_CHROMEVOX_NO_TITLE"> + No title + </message> </messages> </release> </grit> diff --git a/chromium/chrome/browser/resources/chromeos/compiled_resources2.gyp b/chromium/chrome/browser/resources/chromeos/compiled_resources2.gyp index e166fcf476d..20fea0a35fc 100644 --- a/chromium/chrome/browser/resources/chromeos/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/chromeos/compiled_resources2.gyp @@ -16,6 +16,20 @@ 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { + 'target_name': 'internet_config_dialog', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_components/chromeos/network/compiled_resources2.gyp:network_config', + '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', + '<(DEPTH)/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp:cr_policy_network_behavior', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + '<(EXTERNS_GYP):chrome_send', + '<(EXTERNS_GYP):networking_private', + '<(INTERFACES_GYP):networking_private_interface', + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'internet_detail_dialog', 'dependencies': [ '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', diff --git a/chromium/chrome/browser/resources/chromeos/sys_internals/compiled_resources2.gyp b/chromium/chrome/browser/resources/chromeos/sys_internals/compiled_resources2.gyp new file mode 100644 index 00000000000..64b7192275a --- /dev/null +++ b/chromium/chrome/browser/resources/chromeos/sys_internals/compiled_resources2.gyp @@ -0,0 +1,36 @@ +# 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. +{ + 'targets': [ + { + 'target_name': 'index', + 'dependencies': [ + 'constants', + 'externs', + 'types', + 'line_chart/compiled_resources2.gyp:*', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'constants', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'externs', + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'types', + 'dependencies': [ + 'line_chart/compiled_resources2.gyp:data_series', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +} diff --git a/chromium/chrome/browser/resources/chromeos/sys_internals/line_chart/compiled_resources2.gyp b/chromium/chrome/browser/resources/chromeos/sys_internals/line_chart/compiled_resources2.gyp new file mode 100644 index 00000000000..a968ea8f1b0 --- /dev/null +++ b/chromium/chrome/browser/resources/chromeos/sys_internals/line_chart/compiled_resources2.gyp @@ -0,0 +1,63 @@ +# 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. +{ + 'targets': [ + { + 'target_name': 'line_chart', + 'dependencies': [ + 'sub_chart', + 'data_series', + 'scrollbar', + 'menu', + 'constants', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'sub_chart', + 'dependencies': [ + 'unit_label', + 'data_series', + 'constants', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'scrollbar', + 'dependencies': [ + 'constants', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'unit_label', + 'dependencies': [ + 'constants', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'menu', + 'dependencies': [ + 'constants', + 'data_series', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'data_series', + 'dependencies': [ + 'constants', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'constants', + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +} diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/BUILD.gn b/chromium/chrome/browser/resources/chromeos/zip_archiver/BUILD.gn index 68fc2b9ee76..94edb33f30c 100644 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/BUILD.gn +++ b/chromium/chrome/browser/resources/chromeos/zip_archiver/BUILD.gn @@ -27,7 +27,6 @@ copy("zip_archiver_css") { copy("zip_archiver_html") { sources = [ - "html/compressor.html", "html/passphrase-dialog.html", "html/passphrase.html", ] @@ -56,9 +55,10 @@ copy("zip_archiver_js") { "js/app.js", "js/background.js", "js/build-config.js", - "js/compressor-foreground.js", "js/compressor.js", "js/decompressor.js", + "js/file_operation_utils.js", + "js/main.js", "js/passphrase-dialog.js", "js/passphrase-manager.js", "js/request.js", diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ar/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ar/messages.json deleted file mode 100644 index 78454c10907..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ar/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "أداة فتح ملفات ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "فتح ملفات ZIP في تطبيق \"الملفات\".", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "أخفق فتح الأرشيف.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "تم تحميل الأرشيف فعلاً.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "تنسيق الأرشيف غير متوافق، أو الملف تالف.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "الرجاء الانتظار، جارٍ فحص الأرشيف...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "هذا الملف محمي بكلمة المرور", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "كلمة المرور", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "قبول", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "إلغاء", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "تذكر", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/bg/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/bg/messages.json deleted file mode 100644 index fbebba08e62..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/bg/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Разширение за разпакетиране на ZIP файлове", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Отваряне на ZIP файлове в приложението Файлове.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Отварянето на архив не бе успешно.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Архивът вече е свързан.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Форматът на архива не се поддържа или файлът е невалиден.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Моля, изчакайте. Архивът се сканира...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Този файл е защитен с парола", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Парола", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Приемам", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Отказ", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Запомняне", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ca/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ca/messages.json deleted file mode 100644 index 672e463f249..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ca/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Descompressor ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Obriu els fitxers ZIP a l'aplicació Fitxers.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "S'ha produït un error en obrir un arxiu.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "L'arxiu ja està activat.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "El format de l'arxiu no s'admet, o bé el fitxer està malmès.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Espereu, s'està analitzant l'arxiu...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "El fitxer està protegit amb contrasenya", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Contrasenya", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Accepta", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Cancel·la", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Recorda", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/cs/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/cs/messages.json deleted file mode 100644 index b3de9697653..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/cs/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Rozbalování souborů ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Otevírejte soubory ZIP v aplikaci Soubory.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Archiv se nepodařilo otevřít.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Archiv je již připojen.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Formát archivu není podporován nebo je soubor poškozen.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Čekejte prosím, probíhá prohledávání archivu...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Soubor je chráněn heslem", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Heslo", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Přijmout", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Zrušit", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Zapamatovat", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/da/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/da/messages.json deleted file mode 100644 index 0a067817c28..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/da/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP-åbning", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Åbn ZIP-filer i appen Filer.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Et arkiv kunne ikke åbnes.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arkivet er allerede tilsluttet.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Arkivformatet understøttes ikke, eller filen er beskadiget.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Et øjeblik, arkivet scannes…", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Denne fil er beskyttet med adgangskode", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Adgangskode", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Acceptér", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Annuller", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Husk", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/de/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/de/messages.json deleted file mode 100644 index fbd1100b8e6..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/de/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP-Datei-Entpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "ZIP-Dateien in App \"Dateien\" öffnen", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Ein Archiv konnte nicht geöffnet werden.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Das Archiv ist schon bereitgestellt.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Das Archivformat wird nicht unterstützt oder die Datei ist fehlerhaft.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Das Archiv wird geprüft. Bitte warten...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Diese Datei ist passwortgeschützt.", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Passwort", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Akzeptieren", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Abbrechen", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Merken", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/el/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/el/messages.json deleted file mode 100644 index 6d0a376b013..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/el/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Αποσυμπιεστής ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Άνοιγμα αρχείων ZIP στην εφαρμογή \"Αρχεία\".", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Απέτυχε το άνοιγμα ενός αρχείου.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Το αρχείο προσαρτήθηκε ήδη.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Η μορφή του αρχείου δεν υποστηρίζεται ή το αρχείο είναι κατεστραμμένο.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Περιμένετε, γίνεται σάρωση του αρχείου…", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Αυτό το αρχείο προστατεύεται με κωδικό πρόσβασης", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Κωδικός πρόσβασης", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Αποδοχή", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Άκυρο", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Απομνημόνευση", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/en/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/en/messages.json deleted file mode 100644 index d59b942b395..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/en/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP unpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Open ZIP files in Files app.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Failed to open an archive.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "The archive is already mounted.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "The archive format is not supported, or the file is broken.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Please wait, the archive is being scanned...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "This file is password protected", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Password", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Accept", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Cancel", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Remember", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -} diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/en_GB/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/en_GB/messages.json deleted file mode 100644 index 5d2c37199b3..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/en_GB/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP unpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Open ZIP files in Files app.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Failed to open an archive.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "The archive is already mounted.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "The archive format is not supported, or the file is broken.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Please wait, the archive is being scanned...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "This file is password protected", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Password", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Accept", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Cancel", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Remember", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/es/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/es/messages.json deleted file mode 100644 index f8f85fd9c31..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/es/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Descompresor ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Abre archivos ZIP en la aplicación Archivos.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Error al abrir el archivo.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "El archivo ya está activado.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "El formato del archivo no es compatible o el archivo está dañado.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Espera, se está analizando el archivo...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Este archivo está protegido por contraseña", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Contraseña", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Aceptar", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Cancelar", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Recordar", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/es_419/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/es_419/messages.json deleted file mode 100644 index c86051e12b1..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/es_419/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP unpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Abre archivos ZIP en la aplicación Archivos.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Error al abrir un archivo", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Ya se montó el archivo.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "El formato de archivo no es compatible o el archivo está dañado.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Espera, se está analizando el archivo…", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Este archivo está protegido por contraseña", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Contraseña", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Aceptar", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Cancelar", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Recordar", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/et/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/et/messages.json deleted file mode 100644 index 4239a246148..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/et/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP-lahtipakkija", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "ZIP-failide avamine rakenduses Failid.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Arhiivi avamine ebaõnnestus.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arhiiv on juba ühendatud.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Arhiivi formaati ei toetata või on fail katki.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Oodake, arhiivi skannitakse ...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "See fail on parooliga kaitstud", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Parool", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Nõustu", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Tühista", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Pea meeles", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fa/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fa/messages.json deleted file mode 100644 index a353dde68c3..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fa/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "بازکننده ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "فایلهای ZIP را در برنامه Files باز کنید.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "بایگانی باز نشد.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "بایگانی قبلاً نشانده شده است.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "قالب بایگانی پشتیبانی نمیشود یا فایل خراب است.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "لطفاً صبر کنید، بایگانی در حال اسکن شدن است...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "این فایل با گذرواژه محافظت میشود", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "گذرواژه", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "پذیرش", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "لغو", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "به خاطر سپردن", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fi/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fi/messages.json deleted file mode 100644 index cd7f2413ee8..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fi/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP-purkaja", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Avaa ZIP-tiedostot Tiedostot-sovelluksessa.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Arkiston avaaminen epäonnistui.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arkisto on jo otettu käyttöön.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Tätä arkistomuotoa ei tueta tai tiedosto ei toimi.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Odota hetki, arkistoa tarkistetaan…", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Tämä tiedosto on suojattu salasanalla.", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Salasana", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Hyväksy", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Peruuta", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Muista", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fil/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fil/messages.json deleted file mode 100644 index 4fc2c70b781..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fil/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP unpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Magbukas ng mga ZIP file sa Files app.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Hindi nakapagbukas ng isang archive.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Na-mount na ang archive.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Hindi sinusuportahan ang format ng archive o sira ang file.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Mangyaring maghintay, ini-scan ang archive...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Pinoprotektahan ng password ang file na ito", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Password", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Tanggapin", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Kanselahin", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Tandaan", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fr/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fr/messages.json deleted file mode 100644 index b39038e5296..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/fr/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP unpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Ouvrir les fichiers ZIP dans l'application Fichiers", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Échec de l'ouverture d'une archive", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "L'archive est déjà installée.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Le format de l'archive n'est pas compatible, ou le fichier est endommagé.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Veuillez patienter, l'archive est en cours d'analyse…", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Ce fichier est protégé par un mot de passe", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Mot de passe", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Accepter", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Annuler", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Mémoriser", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/he/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/he/messages.json deleted file mode 100644 index 0e805e163a6..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/he/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP unpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "פתח קובצי ZIP ביישום 'קבצים'.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "פתיחת ארכיון נכשלה.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "הארכיון כבר נטען.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "פורמט הארכיון אינו נתמך, או שהקובץ פגום.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "המתן בזמן שהארכיון נסרק...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "הקובץ הזה מוגן באמצעות סיסמה", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "סיסמה", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "קבל", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "בטל", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "זכור", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/hi/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/hi/messages.json deleted file mode 100644 index c1d6330bcb8..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/hi/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ज़िप अनपैकर", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "फ़ाइलें ऐप में ज़िप फ़ाइलें खोलें", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "संग्रह को खोलने में विफल रहा.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "संग्रह पहले से माउंट किया हुआ है.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "संग्रह का प्रारूप समर्थित नहीं है या फ़ाइल त्रुटिपूर्ण है.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "कृपया प्रतीक्षा करें, संग्रह स्कैन किया जा रहा है...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "यह फ़ाइल पासवर्ड द्वारा सुरक्षित है", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "पासवर्ड", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "स्वीकार करें", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "रहने दें", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "याद रखें", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/hr/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/hr/messages.json deleted file mode 100644 index 1f8bc04bee7..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/hr/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Alat za otvaranje ZIP datoteka", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Otvaranje ZIP datoteka u aplikaciji Datoteke.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Otvaranje arhive nije uspjelo.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arhiva je već učitana.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Oblik arhive nije podržan ili je datoteka oštećena.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Pričekajte, arhiva se skenira...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Datoteka je zaštićena zaporkom", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Zaporka", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Prihvaćam", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Odustani", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Zapamti", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/hu/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/hu/messages.json deleted file mode 100644 index 225066cb7e7..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/hu/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP-kicsomagoló", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "ZIP-fájlok megnyitása a Fájlok alkalmazásban.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Nem sikerült megnyitni egy archív elemet.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Az archív elem már be van töltve.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Az archív elem formátuma nem támogatott, vagy hibás a fájl.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Kérjük, várjon. Az archívum beolvasása folyamatban van…", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "A fájl jelszóval védett", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Jelszó", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Elfogadás", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Mégse", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Jegyezze meg", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/id/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/id/messages.json deleted file mode 100644 index 6604927e781..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/id/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP unpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Membuka file ZIP di aplikasi File.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Gagal membuka arsip.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arsip telah terpasang.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Format arsip tidak didukung atau file rusak.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Tunggu, arsip sedang dipindai...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "File ini dilindungi sandi", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Sandi", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Terima", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Batal", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Ingat", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/it/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/it/messages.json deleted file mode 100644 index da83efcc6b2..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/it/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP unpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Apri file ZIP nell'app File.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Apertura di un archivio non riuscita.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "L'archivio è già stato montato.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Il formato dell'archivio non è supportato oppure il file è danneggiato.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Attendi, è in corso la scansione dell'archivio...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Il file è protetto da password", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Password", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Accetta", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Annulla", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Ricorda", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ja/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ja/messages.json deleted file mode 100644 index 551e0baf1e8..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ja/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP 解凍", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "ZIP ファイルをファイルアプリで開きます。", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "アーカイブを開けませんでした。", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "アーカイブはマウント済みです。", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "アーカイブ形式がサポートされていないか、ファイルが破損しています。", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "アーカイブをスキャンしています。しばらくお待ちください...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "パスワードで保護されたファイル", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "パスワード", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "承認", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "キャンセル", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "パスワードを保存", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ko/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ko/messages.json deleted file mode 100644 index 51417dfed39..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ko/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP 압축 해제 프로그램", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "파일 앱에서 ZIP 파일 열기", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "보관 파일을 열지 못했습니다.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "보관 파일이 이미 마운트되어 있습니다.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "보관 파일이 지원되지 않는 형식이거나 파일이 손상되었습니다.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "잠시 기다려 주세요. 보관 파일을 검사하는 중입니다.", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "파일이 비밀번호로 보호되어 있습니다.", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "비밀번호", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "수락", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "취소", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "저장", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/lt/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/lt/messages.json deleted file mode 100644 index b4b9c161bd2..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/lt/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP išpakavimo programa", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Atidarykite ZIP failus naudodami programą „Failai“.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Nepavyko atidaryti archyvo.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Archyvas jau įdėtas.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Archyvo formatas nepalaikomas arba failas yra sugadintas.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Palaukite, archyvas nuskaitomas...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Šis failas apsaugotas slaptažodžiu", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Slaptažodis", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Sutikti", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Atšaukti", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Prisiminti", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/lv/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/lv/messages.json deleted file mode 100644 index 0882e86bc88..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/lv/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP izguvējs", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Atveriet ZIP failus lietotnē Faili.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Neizdevās atvērt arhīvu.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arhīvs jau ir iekļauts.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Arhīva formāts netiek atbalstīts, vai fails ir bojāts.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Lūdzu, uzgaidiet, kamēr notiek arhīva pārbaude...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Šis fails ir aizsargāts ar paroli", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Parole", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Pieņemt", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Atcelt", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Atcerēties", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ms/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ms/messages.json deleted file mode 100644 index 8537a84f3cd..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ms/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Penyahpadat ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Buka fail ZIP dalam apl Fail.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Gagal membuka arkib.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arkib sudah dilekapkan.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Format arkib tidak disokong atau fail rosak.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Sila tunggu, arkib sedang diimbas…", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Fail ini dilindungi kata laluan", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Kata laluan", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Terima", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Batal", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Ingat", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/nl/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/nl/messages.json deleted file mode 100644 index dd970539829..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/nl/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP-uitpakker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Open zipbestanden in de app Bestanden.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Kan een archief niet openen.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Het archief is al gekoppeld.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "De archiefindeling wordt niet ondersteund of het bestand is beschadigd.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Het archief wordt gescand. Een ogenblik geduld…", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Dit bestand is beveiligd met een wachtwoord", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Wachtwoord", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Accepteren", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Annuleren", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Onthouden", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/no/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/no/messages.json deleted file mode 100644 index cd5f57bc19d..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/no/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP-utpakker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Åpne ZIP-filer i Filer-appen.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Et arkiv kunne ikke åpnes.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arkivet er allerede åpnet.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Arkivformatet støttes ikke, eller filen er ødelagt.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Vent litt – arkivet skannes …", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Denne filen er passordbeskyttet.", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Passord", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Godta", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Avbryt", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Husk", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/pl/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/pl/messages.json deleted file mode 100644 index ae8556eca7a..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/pl/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Program do rozpakowywania plików ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Otwieraj pliki ZIP w aplikacji Pliki.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Nie udało się otworzyć archiwum.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Archiwum zostało już podłączone.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Format archiwum nie jest obsługiwany albo plik jest uszkodzony.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Zaczekaj, trwa skanowanie archiwum...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Ten plik jest chroniony hasłem", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Hasło", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Zaakceptuj", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Anuluj", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Pamiętaj", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/pt_BR/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/pt_BR/messages.json deleted file mode 100644 index 01dbfe87349..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/pt_BR/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP unpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Abra arquivos ZIP no app \"Arquivos\".", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Falha ao abrir um arquivo.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "O arquivo já está ativado.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "O formato do arquivo não é compatível ou o arquivo está corrompido.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Aguarde. O arquivo está sendo verificado...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Este arquivo é protegido por senha", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Senha", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Aceitar", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Cancelar", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Lembrar", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/pt_PT/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/pt_PT/messages.json deleted file mode 100644 index dc86b5aac07..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/pt_PT/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP unpacker", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Abre ficheiros ZIP na aplicação Ficheiros.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Falha ao abrir um arquivo.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "O arquivo já está montado.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "O formato do arquivo não é suportado ou o ficheiro está danificado.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Aguarde enquanto o arquivo está a ser analisado...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Este ficheiro está protegido por palavra-passe", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Palavra-passe", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Aceitar", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Cancelar", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Memorizar", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ro/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ro/messages.json deleted file mode 100644 index 3f7ec88107a..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ro/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Dezarhivator ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Deschideți fișiere ZIP în aplicația Fișiere.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Arhiva nu a putut fi deschisă.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arhiva este deja montată.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Formatul arhivei nu este acceptat sau fișierul este deteriorat.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Așteptați, se scanează arhiva...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Fișierul este protejat prin parolă", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Parolă", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Acceptați", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Anulați", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Reține", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ru/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ru/messages.json deleted file mode 100644 index 5bba0e0fff7..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/ru/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Распаковщик ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Открывает ZIP-архивы в приложении \"Файлы\".", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Не удалось открыть архив", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Архив уже существует.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Архив поврежден, или его формат не поддерживается.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Сканирование архива...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Файл защищен паролем", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Пароль", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "ОК", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Отмена", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Запомнить", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sk/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sk/messages.json deleted file mode 100644 index 18b16d80cd3..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sk/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Rozbaľovanie priečinkov ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Otvárajte súbory ZIP v aplikácii Súbory.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Archív sa nepodarilo otvoriť", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Archív je už pripojený", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Formát archívu nie je podporovaný alebo je súbor poškodený", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Čakajte, prehľadáva sa archív...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Súbor je chránený heslom", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Heslo", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Prijať", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Zrušiť", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Zapamätať", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sl/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sl/messages.json deleted file mode 100644 index 9ba38b9dc06..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sl/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Razširitev za odpiranje arhivov ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Odpiranje datotek ZIP v aplikaciji Datoteke.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Arhiva ni bilo mogoče odpreti.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arhiv je že vpet.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Oblika zapisa arhiva ni podprta ali pa je datoteka poškodovana.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Počakajte, poteka preverjanje arhiva ...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Ta datoteka je zaščitena z geslom", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Geslo", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Sprejmi", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Prekliči", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Shrani", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sr/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sr/messages.json deleted file mode 100644 index 58711406c26..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sr/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP отпакивач", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Отварајте ZIP датотеке у апликацији Датотеке.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Отварање архиве није успело.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Архива је већ учитана", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Формат архиве није подржан или је датотека оштећена.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Сачекајте, архива се скенира...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Ова датотека је заштићена лозинком", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Лозинка", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Прихвати", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Откажи", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Запамти", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sv/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sv/messages.json deleted file mode 100644 index 7264c317aa0..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/sv/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Uppackningsverktyg för zip-filer", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Öppna zip-filer i appen Filer.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Det gick inte att öppna arkivet.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arkivet har redan monterats.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Arkivformatet stöds inte, eller så är filen trasig.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Arkivet läses in. Vänta ...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Filen är lösenordsskyddad", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Lösenord", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Tacka ja", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Avbryt", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Kom ihåg", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/th/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/th/messages.json deleted file mode 100644 index 05af54ecce1..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/th/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "โปรแกรมเปิดไฟล์ ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "เปิดไฟล์ ZIP ในแอป Files", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "ไม่สามารถเปิดไฟล์", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "ไฟล์นี้เปิดอยู่แล้ว", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "ไม่สนับสนุนไฟล์รูปแบบนี้ หรือไฟล์เสียหาย", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "โปรดรอสักครู่ ระบบกำลังสแกนไฟล์...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "ไฟล์นี้มีการป้องกันด้วยรหัสผ่าน", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "รหัสผ่าน", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "ยอมรับ", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "ยกเลิก", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "จำ", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/tr/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/tr/messages.json deleted file mode 100644 index 010aca4e4e1..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/tr/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP paket açıcı", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Zip dosyalarını Dosyalar uygulamasında açın.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Arşiv açılamadı.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Arşiv zaten eklenmiş.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Arşiv biçimi desteklenmiyor veya dosya bozuk.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Arşiv taranırken lütfen bekleyin...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Bu dosya şifre korumalı", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Şifre", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Kabul et", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "İptal", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Hatırla", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/uk/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/uk/messages.json deleted file mode 100644 index e5abb4144c0..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/uk/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Розпакувальник ZIP-архівів", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Відкривання ZIP-архівів у додатку Файли.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Не вдалося відкрити архів.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Архів уже підключено.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Формат архіву не підтримується або файл пошкоджено.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Сканування архіву. Зачекайте…", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Цей файл захищено паролем", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Пароль", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Прийняти", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Скасувати", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Запам’ятати", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/vi/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/vi/messages.json deleted file mode 100644 index d75cd0b9080..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/vi/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "Trình giải nén ZIP", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "Mở tệp ZIP trong ứng dụng Tệp.", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "Không mở được tệp lưu trữ.", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "Tệp lưu trữ đã được kết nối.", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "Định dạng lưu trữ không được hỗ trợ hoặc tệp bị hỏng.", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "Vui lòng đợi, tệp lưu trữ đang được quét...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "Tệp này được bảo vệ bằng mật khẩu", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "Mật khẩu", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "Chấp nhận", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "Hủy", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "Nhớ", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/zh_CN/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/zh_CN/messages.json deleted file mode 100644 index f4dfb2f4697..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/zh_CN/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP 解压缩程序", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "在“文件”应用中打开 ZIP 文件。", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "未能打开某个归档文件。", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "该归档文件已打开。", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "该归档文件的格式不受支持,或该文件已损坏。", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "请稍候,正在扫描该归档文件…", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "该文件受密码保护", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "密码", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "接受", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "取消", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "记住密码", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/zh_TW/messages.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/zh_TW/messages.json deleted file mode 100644 index ef4d3ad0f95..00000000000 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/_locales/zh_TW/messages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": { - "message": "ZIP 解壓縮工具", - "description": "Name of the ZIP unpacker app." - }, - "description": { - "message": "可讓您在「檔案」應用程式中開啟 ZIP 檔案。", - "description": "Short description of the ZIP unpacker app." - }, - "mountErrorTitle": { - "message": "無法開啟封存檔案。", - "description": "Title of a notification with a mounting error message." - }, - "existsErrorMessage": { - "message": "這個封存檔案已掛接。", - "description": "Error message when the archive is already mounted." - }, - "otherErrorMessage": { - "message": "您使用了不支援的封存格式,或者檔案已毀損。", - "description": "Error message when opening an archive fails because of an unknown reason." - }, - "mountingMessage": { - "message": "系統正在掃描封存檔案,請稍候...", - "description": "Message shown when opening an archive takes long time." - }, - "passphraseTitle": { - "message": "這個檔案受到密碼保護", - "description": "Title of the password input dialog." - }, - "passphraseInputLabel": { - "message": "密碼", - "description": "Label of the password input field." - }, - "passphraseAccept": { - "message": "接受", - "description": "Short name of the password accept button." - }, - "passphraseCancel": { - "message": "取消", - "description": "Short name of the password cancel button." - }, - "passphraseRemember": { - "message": "記住密碼", - "description": "Title for of a checkbox whether the password should be remembered. Please keep it as short." - } -}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/chromeos/zip_archiver/manifest.json b/chromium/chrome/browser/resources/chromeos/zip_archiver/manifest.json index 49ef7c0a16d..456a43640de 100644 --- a/chromium/chrome/browser/resources/chromeos/zip_archiver/manifest.json +++ b/chromium/chrome/browser/resources/chromeos/zip_archiver/manifest.json @@ -1,20 +1,27 @@ { // chrome-extension://dmboannefpncccogfdikhmhpmdnddgoe "key": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxGxJCOLUzHIYc812NFoBC1eV8PhOTuF6he3gSuqzxckUyrDLdl5++DAd1AkQkv6i8SSMWFvDKLg2b+zfCOwk6P7uu3tqNavXXy61Okaq5HKF3xhciNDl4zF6ZlegvE9AhJOTo2eCHVIMS0+YuK5hyno/+xMwN4byvsrOYXQnhcJeOHxkFb9TfVUb3SOBgl4pBZ7+EIMNntEvzY7mxjBzOgnCjBePvwnoMRyAqljCJarz2WSbUOLP3yoCuH9vPKj+0D6hF1woXmd6qBr0ln/7tHdbr1cYmkosfFuJO2y6d00FAJY/G5L6o8JAEBbWG5D0qELt+aBjkG0uos5gcR4ZPAgMBAAECggEBAK3aIjFJU25J6MjQiRvvY5a4O56bnUIb8SDZgAP6pbwZ7R2R9hiaN6AqVMOiptvgHDZAISYU/OerD4b3s0OCCkvYtlcxwh6iSZQ9BvIighFWrpZRqPHVjDktfQuNIS/dZiiy+9Yr0oFmD4jS45idCPgy+K0h6CEUX9GlPTEq24ElECDwQHVyB9LHdenleCdvldIEDxf6/D+zkc/PmCPlZPfwdppK6wgH2GvgqbxV+OoSnNp0XhNinjCN37P5yAo4xEi0UGOxOwkNGkJn0V5bYjH6/JHzmdVH74D40N4/Fcy0bC79oFGeiP0ZzW8AAArfIxbxStodWlBOCsTVtvi4RMECgYEA2pyZRThGx4OH8uXCC94LkdpVjKvLvbUlIVd2zk3UEFpWujgmAI+erkAaE1JSlcJpFNSlfonTX1vQuMgTOfnK7soy4677P1CMQH++GxjMWRIAQsMyx7vLtKOISr5/vQQKAyuFmxzt9xbMOmPzqWxwkuuiF74GtPgE5VXslhvsoyECgYEAz2U7L6YS4u2aMRK4DMDxcf/hZ3BxwHmUl5euknRNcaFJSdv6392y8w3t9Lz7sp8CK56GADXL1bmLrDgg2tlL82f60rtPd6IOoJk10uMmCnyjbZh7aJzuw1CTSs+dwi6qpGUB4YbJn8R2AN79SHxUb4dwVOh4lHeNa415Wka+a28CgYA3Vf5iDB22cO/fpxLYSCtrjvWqtu3KpmiwqOAU1pSAUy2y03WjHLeQ6f7vtx3adKx+rlj5z89mSuppa5OaUEVy7lG1WlyUqUHnLa6kU0GepjTUsW5QKpQktGRSbygMY1JZfRHDsq31ppqpiRVrZFyWg/iyw9IUytcKahaJ5KWgoQKBgFbgY/ugyNaQi3+1BK4rALktZAGNo8jp5SnfWzx0RaCs3GN5J80xNG4GTsCvjYwUebdF74IVBu7fi7e3x2OFlQBAdVxjJHXLx+7UXyyZBG1uKpOVRVTcMFRW42x6Le6S196HhVMwwDMR/BB/WIBNvJz/kjmvLBudPPtpxwTfD5M3AoGBALrrXX4QwqBiq4q09SPKoeOwlV35QETUhQaAKKag9aSrNMONcf77TXUBZ0d9Z+tabHLTGGa6E7q2BL82NdZSZvVeVWA+KaE4ezW2t5KyZqg14Cc0uY9Xys9VkFcVgMqsvtkUzDvAVJcmNAgcrMIEiapUR6LPrneLLXH1ikOt+hM8", - // TODO(takise): translation - "name": "ZIP archiver", - "version": "0.76", + "name": "Zip Archiver", + "version": "1.0", "manifest_version": 2, "minimum_chrome_version": "44.0.2400.0", - // TODO(takise): translation - "description": "ZIP archiver", + "description": "Zip Archiver - Open and pack ZIP files in Files app.", "default_locale": "en", "display_in_launcher": false, + "incognito": "split", "permissions": [ "alwaysOnTopWindows", "chrome://resources/", "fileSystemProvider", - {"fileSystem": ["retainEntries", "write", "directory"]}, + "fileManagerPrivate", + { + "fileSystem": [ + "requestFileSystem", + "retainEntries", + "write", + "directory" + ] + }, "notifications", "storage" ], @@ -47,6 +54,7 @@ "js/unpacker.js", "js/app.js", "js/background.js", + "js/file_operation_utils.js", "js/compressor.js", "js/decompressor.js", "js/passphrase-manager.js", @@ -57,4 +65,4 @@ }, "content_security_policy": "default-src 'none'; script-src chrome://resources; style-src 'unsafe-inline' chrome://resources;" } -} +}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/component_extension_resources.grd b/chromium/chrome/browser/resources/component_extension_resources.grd index 790e53cb609..6ec5019a7ff 100644 --- a/chromium/chrome/browser/resources/component_extension_resources.grd +++ b/chromium/chrome/browser/resources/component_extension_resources.grd @@ -132,7 +132,6 @@ <include name="IDR_FIRST_RUN_DIALOG_ICON_256" file="chromeos/first_run/app/icon/256.png" type="BINDATA" /> <include name="IDR_ARC_SUPPORT_BACKGROUND_JS" file="chromeos/arc_support/background.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_ARC_SUPPORT_MAIN_CSS" file="chromeos/arc_support/main.css" type="BINDATA" /> - <include name="IDR_ARC_SUPPORT_LSO_CSS" file="chromeos/arc_support/lso.css" type="BINDATA" /> <include name="IDR_ARC_SUPPORT_PLAYSTORE_CSS" file="chromeos/arc_support/playstore.css" type="BINDATA" /> <include name="IDR_ARC_SUPPORT_PLAYSTORE_JS" file="chromeos/arc_support/playstore.js" type="BINDATA" /> <include name="IDR_ARC_SUPPORT_PLAYSTORE_LOGO" file="chromeos/arc_support/icon/playstore.svg" type="BINDATA" /> diff --git a/chromium/chrome/browser/resources/cryptotoken/compiled_resources2.gyp b/chromium/chrome/browser/resources/cryptotoken/compiled_resources2.gyp new file mode 100644 index 00000000000..a84a888dcca --- /dev/null +++ b/chromium/chrome/browser/resources/cryptotoken/compiled_resources2.gyp @@ -0,0 +1,47 @@ +# 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. +{ + 'targets': [ + { + 'target_name': 'errorcodes', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'b64', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'approvedorigins', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'closeable', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'countdown', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'inherits', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'origincheck', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'sha256', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'textfetcher', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'timer', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ] +} diff --git a/chromium/chrome/browser/resources/uber/compiled_resources2.gyp b/chromium/chrome/browser/resources/download_internals/compiled_resources2.gyp index 960d088d392..13baad13e0e 100644 --- a/chromium/chrome/browser/resources/uber/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/download_internals/compiled_resources2.gyp @@ -1,39 +1,31 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# 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. { 'targets': [ { - 'target_name': 'uber_frame', + 'target_name': 'download_internals', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util', - '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:focus_outline_manager', - 'uber_utils', + 'download_internals_browser_proxy', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'uber_utils', + 'target_name': 'download_internals_browser_proxy', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util', - '<(EXTERNS_GYP):chrome_send', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'uber', + 'target_name': 'download_internals_visuals', 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util', - 'uber_utils', - '<(EXTERNS_GYP):chrome_send', + 'download_internals_browser_proxy', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, diff --git a/chromium/chrome/browser/resources/download_internals/download_internals.css b/chromium/chrome/browser/resources/download_internals/download_internals.css new file mode 100644 index 00000000000..63dab4dbfc0 --- /dev/null +++ b/chromium/chrome/browser/resources/download_internals/download_internals.css @@ -0,0 +1,41 @@ +/* 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. */ + +h1 { + color: rgb(74, 142, 230); + margin: 0; + padding: 0; +} + +.status { + font-size: 15px; +} + +.service-entry-new { + background-color: rgb(242, 242, 242) +} + +.service-entry-available { + background-color: rgb(242, 242, 242) +} + +.service-entry-active { + background-color: rgb(214, 249, 245) +} + +.service-entry-paused { + background-color: rgb(240, 200, 125) +} + +.service-entry-success { + background-color: rgb(157, 239, 133) +} + +.service-entry-fail { + background-color: rgb(215, 180, 180) +} + +.service-entry-blocked { + background-color: rgb(239, 235, 133) +}
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/download_internals/download_internals.html b/chromium/chrome/browser/resources/download_internals/download_internals.html new file mode 100644 index 00000000000..259ed42fc33 --- /dev/null +++ b/chromium/chrome/browser/resources/download_internals/download_internals.html @@ -0,0 +1,110 @@ +<!doctype html> +<html lang="en" dir="ltr"> + <head> + <meta charset="utf-8"> + <title>Download Internals</title> + <meta name="viewport" content="width=device-width"> + <link rel="stylesheet" href="chrome://resources/css/list.css"> + <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> + <link rel="stylesheet" href="download_internals.css"> + + <link rel="import" href="chrome://resources/html/cr.html"> + <link rel="import" href="chrome://resources/html/cr/ui/list_item.html"> + <link rel="import" href="chrome://resources/html/cr/ui/list.html"> + <link rel="import" href="chrome://resources/html/load_time_data.html"> + <link rel="import" href="chrome://resources/html/util.html"> + + <script src="strings.js"></script> + <script src="download_internals_browser_proxy.js"></script> + <script src="download_internals.js"></script> + <script src="download_internals_visuals.js"></script> + </head> + <body> + <h1>Download Internals</h1> + <h4>Service State</h4> + <div> + State: <span id="service-state" class="status"></span> + Model: <span id="service-status-model" class="status"></span> + Driver: <span id="service-status-driver" class="status"></span> + File Monitor: <span id="service-status-file" class="status"></span> + </div> + <h4>Entry Requests</h4> + <div id="download-service-request-info"> + <table class="styled-table"> + <thead> + <tr> + <th>Result</th> + <th>Client</th> + <th>ID</th> + </tr> + </thead> + <tbody> + <tr jsselect="requests" + jsvalues=".className: downloadInternalsVisuals.getServiceRequestClass($this)"> + <td jscontent="result"></td> + <td jscontent="client"></td> + <td jscontent="guid"></td> + </tr> + </tbody> + </table> + </div> + <h4>Completed/Failed Downloads</h4> + <div id="download-service-finished-entries-info"> + <table class="styled-table"> + <thead> + <tr> + <th>Result</th> + <th>Client</th> + <th>ID</th> + <th>URL</th> + <th>Size</th> + </tr> + </thead> + <tbody> + <tr jsselect="entries" + jsvalues=".className: downloadInternalsVisuals.getFinishedServiceEntryClass($this)"> + <td jscontent="result"></td> + <td jscontent="client"></td> + <td jscontent="guid"></td> + <td> + <div jscontent="url"></div> + <div jscontent="file_path"></div> + </td> + <td jscontent="bytes_downloaded"></td> + </tr> + </tbody> + </table> + </div> + <h4>In-Progress Downloads</h4> + <div id="download-service-ongoing-entries-info"> + <table class="styled-table"> + <thead> + <tr> + <th>State</th> + <th>Client</th> + <th>ID</th> + <th>URL</th> + <th>Bytes Downloaded</th> + </tr> + </thead> + <tbody> + <tr jsselect="entries" + jsvalues=".className: downloadInternalsVisuals.getOngoingServiceEntryClass($this)"> + <td> + <div jscontent="state"></div> + <div jsdisplay="typeof driver != 'undefined'"> + <span jscontent="driver.state"></span> + </div> + </td> + <td jscontent="client"></td> + <td jscontent="guid"></td> + <td jscontent="url"></td> + <td jscontent="bytes_downloaded"></td> + </tr> + </tbody> + </table> + </div> + <script src="chrome://resources/js/i18n_template.js"></script> + <script src="chrome://resources/js/jstemplate_compiled.js"></script> + </body> +</html> diff --git a/chromium/chrome/browser/resources/download_internals/download_internals.js b/chromium/chrome/browser/resources/download_internals/download_internals.js new file mode 100644 index 00000000000..62f1f1f0ab8 --- /dev/null +++ b/chromium/chrome/browser/resources/download_internals/download_internals.js @@ -0,0 +1,135 @@ +// 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. + +cr.define('downloadInternals', function() { + 'use strict'; + + /** @type {!downloadInternals.DownloadInternalsBrowserProxy} */ + var browserProxy = + downloadInternals.DownloadInternalsBrowserProxyImpl.getInstance(); + + /** @type {!Array<ServiceEntry>} */ + var ongoingServiceEntries = []; + + /** @type {!Array<ServiceEntry>} */ + var finishedServiceEntries = []; + + /** @type {!Array<ServiceRequest>} */ + var serviceRequests = []; + + /** + * @param {!Array<ServiceEntry>} list A list to remove the entry from. + * @param {string} guid The guid to remove from the list. + */ + function removeGuidFromList(list, guid) { + var index = list.findIndex(entry => entry.guid == guid); + if (index != -1) + list.splice(index, 1); + } + + /** + * Replaces the ServiceEntry specified by guid in the list or, if it's not + * found, adds a new entry. + * @param {!Array<ServiceEntry>} list A list to update. + * @param {!ServiceEntry} newEntry The new entry. + */ + function addOrUpdateEntryByGuid(list, newEntry) { + var index = list.findIndex(entry => entry.guid == newEntry.guid); + if (index != -1) + list[index] = newEntry; + else + list.unshift(newEntry); + } + + function updateEntryTables() { + var ongoingInput = new JsEvalContext({entries: ongoingServiceEntries}); + jstProcess(ongoingInput, $('download-service-ongoing-entries-info')); + + var finishedInput = new JsEvalContext({entries: finishedServiceEntries}); + jstProcess(finishedInput, $('download-service-finished-entries-info')); + } + + /** + * @param {!ServiceStatus} state The current status of the download service. + */ + function onServiceStatusChanged(state) { + $('service-state').textContent = state.serviceState; + $('service-status-model').textContent = state.modelStatus; + $('service-status-driver').textContent = state.driverStatus; + $('service-status-file').textContent = state.fileMonitorStatus; + } + + /** + * @param {!Array<!ServiceEntry>} entries A list entries currently tracked by + * the download service. + */ + function onServiceDownloadsAvailable(entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (entry.state == ServiceEntryState.COMPLETE) { + finishedServiceEntries.unshift(entry); + } else { + ongoingServiceEntries.unshift(entry); + } + } + + updateEntryTables(); + } + + /** + * @param {!ServiceEntry} entry The new state for a particular download + * service entry. + */ + function onServiceDownloadChanged(entry) { + if (entry.state == ServiceEntryState.COMPLETE) { + removeGuidFromList(ongoingServiceEntries, entry.guid); + addOrUpdateEntryByGuid(finishedServiceEntries, entry); + } else { + addOrUpdateEntryByGuid(ongoingServiceEntries, entry); + } + + updateEntryTables(); + } + + /** + * @param {!ServiceEntry} entry The new state for a failed download service + * entry. + */ + function onServiceDownloadFailed(entry) { + removeGuidFromList(ongoingServiceEntries, entry.guid); + addOrUpdateEntryByGuid(finishedServiceEntries, entry); + + updateEntryTables(); + } + + /** + * @param {!ServiceRequest} request The state for a newly issued download + * service request. + */ + function onServiceRequestMade(request) { + serviceRequests.unshift(request); + var input = new JsEvalContext({requests: serviceRequests}); + jstProcess(input, $('download-service-request-info')); + } + + function initialize() { + // Register all event listeners. + cr.addWebUIListener('service-status-changed', onServiceStatusChanged); + cr.addWebUIListener( + 'service-downloads-available', onServiceDownloadsAvailable); + cr.addWebUIListener('service-download-changed', onServiceDownloadChanged); + cr.addWebUIListener('service-download-failed', onServiceDownloadFailed); + cr.addWebUIListener('service-request-made', onServiceRequestMade); + + // Kick off requests for the current system state. + browserProxy.getServiceStatus().then(onServiceStatusChanged); + browserProxy.getServiceDownloads().then(onServiceDownloadsAvailable); + } + + return { + initialize: initialize, + }; +}); + +document.addEventListener('DOMContentLoaded', downloadInternals.initialize); diff --git a/chromium/chrome/browser/resources/download_internals/download_internals_browser_proxy.js b/chromium/chrome/browser/resources/download_internals/download_internals_browser_proxy.js new file mode 100644 index 00000000000..89642e117d5 --- /dev/null +++ b/chromium/chrome/browser/resources/download_internals/download_internals_browser_proxy.js @@ -0,0 +1,131 @@ +// 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. + +/** + * Contains the possible states a ServiceEntry can be in. + * @enum {string} + */ +var ServiceEntryState = { + NEW: 'NEW', + AVAILABLE: 'AVAILABLE', + ACTIVE: 'ACTIVE', + PAUSED: 'PAUSED', + COMPLETE: 'COMPLETE', +}; + +/** + * Contains the possible states a ServiceEntry's driver can be in. + * @enum {string} + */ +var DriverEntryState = { + IN_PROGRESS: 'IN_PROGRESS', + COMPLETE: 'COMPLETE', + CANCELLED: 'CANCELLED', + INTERRUPTED: 'INTERRUPTED', +}; + +/** + * Contains the possible results a ServiceEntry can have. + * @enum {string} + */ +var ServiceEntryResult = { + SUCCEED: 'SUCCEED', + FAIL: 'FAIL', + ABORT: 'ABORT', + TIMEOUT: 'TIMEOUT', + UNKNOWN: 'UNKNOWN', + CANCEL: 'CANCEL', + OUT_OF_RETRIES: 'OUT_OF_RETRIES', + OUT_OF_RESUMPTIONS: 'OUT_OF_RESUMPTIONS', +}; + +/** + * Contains the possible results of a ServiceRequest. + * @enum {string} + */ +var ServiceRequestResult = { + ACCEPTED: 'ACCEPTED', + BACKOFF: 'BACKOFF', + UNEXPECTED_CLIENT: 'UNEXPECTED_CLIENT', + UNEXPECTED_GUID: 'UNEXPECTED_GUID', + CLIENT_CANCELLED: 'CLIENT_CANCELLED', + INTERNAL_ERROR: 'INTERNAL_ERROR', +}; + +/** + * @typedef {{ + * serviceState: string, + * modelStatus: string, + * driverStatus: string, + * fileMonitorStatus: string + * }} + */ +var ServiceStatus; + +/** + * @typedef {{ + * client: string, + * guid: string, + * state: !ServiceEntryState, + * url: string, + * bytes_downloaded: number, + * result: (!ServiceEntryResult|undefined), + * driver: { + * state: !DriverEntryState, + * paused: boolean, + * done: boolean + * } + * }} + */ +var ServiceEntry; + +/** + * @typedef {{ + * client: string, + * guid: string, + * result: !ServiceRequestResult + * }} + */ +var ServiceRequest; + +cr.define('downloadInternals', function() { + /** @interface */ + class DownloadInternalsBrowserProxy { + /** + * Gets the current status of the Download Service. + * @return {!Promise<ServiceStatus>} A promise firing when the service + * status is fetched. + */ + getServiceStatus() {} + + /** + * Gets the current list of downloads the Download Service is aware of. + * @return {!Promise<!Array<!ServiceEntry>>} A promise firing when the list + * of downloads is fetched. + */ + getServiceDownloads() {} + } + + /** + * @implements {downloadInternals.DownloadInternalsBrowserProxy} + */ + class DownloadInternalsBrowserProxyImpl { + /** @override */ + getServiceStatus() { + return cr.sendWithPromise('getServiceStatus'); + } + + /** @override */ + getServiceDownloads() { + return cr.sendWithPromise('getServiceDownloads'); + } + } + + cr.addSingletonGetter(DownloadInternalsBrowserProxyImpl); + + return { + DownloadInternalsBrowserProxy: DownloadInternalsBrowserProxy, + DownloadInternalsBrowserProxyImpl: DownloadInternalsBrowserProxyImpl + }; +}); diff --git a/chromium/chrome/browser/resources/download_internals/download_internals_visuals.js b/chromium/chrome/browser/resources/download_internals/download_internals_visuals.js new file mode 100644 index 00000000000..c3945574222 --- /dev/null +++ b/chromium/chrome/browser/resources/download_internals/download_internals_visuals.js @@ -0,0 +1,55 @@ +// 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. + +cr.define('downloadInternalsVisuals', function() { + 'use strict'; + + function getOngoingServiceEntryClass(entry) { + switch (entry.state) { + case ServiceEntryState.NEW: + return 'service-entry-new'; + case ServiceEntryState.AVAILABLE: + return 'service-entry-available'; + case ServiceEntryState.ACTIVE: + if (entry.driver == undefined || !entry.driver.paused || + entry.driver.state == DriverEntryState.INTERRUPTED) + return 'service-entry-active'; + else + return 'service-entry-blocked'; + case ServiceEntryState.PAUSED: + return 'service-entry-paused'; + case ServiceEntryState.COMPLETE: + return 'service-entry-success'; + default: + return ''; + } + } + + function getFinishedServiceEntryClass(entry) { + switch (entry.result) { + case ServiceEntryResult.SUCCEED: + return 'service-entry-success'; + default: + return 'service-entry-fail'; + } + } + + function getServiceRequestClass(request) { + switch (request.result) { + case ServiceRequestResult.ACCEPTED: + return 'service-entry-success'; + case ServiceRequestResult.BACKOFF: + case ServiceRequestResult.CLIENT_CANCELLED: + return 'service-entry-blocked'; + default: + return 'service-entry-fail'; + } + } + + return { + getOngoingServiceEntryClass: getOngoingServiceEntryClass, + getFinishedServiceEntryClass: getFinishedServiceEntryClass, + getServiceRequestClass: getServiceRequestClass + }; +});
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/extensions/compiled_resources2.gyp b/chromium/chrome/browser/resources/extensions/compiled_resources2.gyp index 86bdc2d6130..c2f646198d0 100644 --- a/chromium/chrome/browser/resources/extensions/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/extensions/compiled_resources2.gyp @@ -4,24 +4,10 @@ { 'targets': [ { - 'target_name': 'shortcut_util', - 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { - 'target_name': 'drag_and_drop_handler', + 'target_name': 'extensions', 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:drag_wrapper', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:webui_listener_tracker', ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { - 'target_name': 'extensions', 'variables': { 'script_args': ['--custom_sources'], 'source_files': [ @@ -64,12 +50,12 @@ 'extension_command_list.js', '<(DEPTH)/ui/webui/resources/js/cr/ui/controlled_indicator.js', 'extension_error_overlay.js', - 'drag_and_drop_handler.js', + '../md_extensions/drag_and_drop_handler.js', '<(DEPTH)/ui/webui/resources/js/cr/ui/list_selection_model.js', '<(DEPTH)/third_party/jstemplate/jstemplate.js', 'chromeos/kiosk_app_list.js', '<(DEPTH)/third_party/closure_compiler/externs/developer_private.js', - 'shortcut_util.js', + '../md_extensions/shortcut_util.js', 'pack_extension_overlay.js', ], }, diff --git a/chromium/chrome/browser/resources/extensions/drag_and_drop_handler.html b/chromium/chrome/browser/resources/extensions/drag_and_drop_handler.html deleted file mode 100644 index d5665f77bec..00000000000 --- a/chromium/chrome/browser/resources/extensions/drag_and_drop_handler.html +++ /dev/null @@ -1,2 +0,0 @@ -<link rel="import" href="chrome://resources/html/cr.html"> -<script src="chrome://extensions/drag_and_drop_handler.js"></script> diff --git a/chromium/chrome/browser/resources/extensions/extension_command_list.js b/chromium/chrome/browser/resources/extensions/extension_command_list.js index eca1797c75b..1aa48e3052f 100644 --- a/chromium/chrome/browser/resources/extensions/extension_command_list.js +++ b/chromium/chrome/browser/resources/extensions/extension_command_list.js @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// <include src="shortcut_util.js"> +// <include src="../md_extensions/shortcut_util.js"> cr.define('extensions', function() { 'use strict'; @@ -378,8 +378,8 @@ cr.define('extensions', function() { */ handleSetCommandScope_: function(event) { var parsed = this.parseElementId_('setCommandScope', event.target.id); - var element = document.getElementById( - 'setCommandScope-' + parsed.extensionId + '-' + parsed.commandName); + var element = + $('setCommandScope-' + parsed.extensionId + '-' + parsed.commandName); var scope = element.selectedIndex == 1 ? chrome.developerPrivate.CommandScope.GLOBAL : chrome.developerPrivate.CommandScope.CHROME; diff --git a/chromium/chrome/browser/resources/extensions/extensions.html b/chromium/chrome/browser/resources/extensions/extensions.html index bcca289b8c7..40d6ce3f016 100644 --- a/chromium/chrome/browser/resources/extensions/extensions.html +++ b/chromium/chrome/browser/resources/extensions/extensions.html @@ -19,7 +19,7 @@ <link rel="stylesheet" href="chrome://resources/css/overlay.css"> <link rel="stylesheet" href="chrome://resources/css/spinner.css"> <link rel="stylesheet" href="chrome://resources/css/trash.css"> -<link rel="stylesheet" href="../uber/uber_shared.css"> +<link rel="stylesheet" href="../uber_shared.css"> <script src="chrome://resources/js/action_link.js"></script> <script src="chrome://resources/js/cr.js"></script> @@ -48,6 +48,8 @@ </script> <script src="chrome://resources/js/cr/ui/list_item.js"></script> <script src="chrome://resources/js/cr/ui/list.js"></script> +<script src="chrome://resources/js/promise_resolver.js"></script> +<script src="chrome://resources/js/webui_listener_tracker.js"></script> </if> <script src="chrome://extensions/extensions.js"></script> diff --git a/chromium/chrome/browser/resources/extensions/extensions.js b/chromium/chrome/browser/resources/extensions/extensions.js index e6b511b28f1..d46ea50ad19 100644 --- a/chromium/chrome/browser/resources/extensions/extensions.js +++ b/chromium/chrome/browser/resources/extensions/extensions.js @@ -4,7 +4,7 @@ // <include src="../../../../ui/webui/resources/js/cr/ui/focus_row.js"> // <include src="../../../../ui/webui/resources/js/cr/ui/focus_grid.js"> -// <include src="drag_and_drop_handler.js"> +// <include src="../md_extensions/drag_and_drop_handler.js"> // <include src="extension_code.js"> // <include src="extension_commands_overlay.js"> // <include src="extension_error_overlay.js"> diff --git a/chromium/chrome/browser/resources/feedback/html/default.html b/chromium/chrome/browser/resources/feedback/html/default.html index debe0a53e2b..8f839557466 100644 --- a/chromium/chrome/browser/resources/feedback/html/default.html +++ b/chromium/chrome/browser/resources/feedback/html/default.html @@ -40,7 +40,7 @@ </div> </div> <div id="content-pane" class="content"> - <textarea id="description-text" aria-labelledby="title-bar"></textarea> + <textarea id="description-text" aria-labelledby="page-title"></textarea> <div> <p id="additional-info-label" i18n-content="additionalInfo"><p> </div> @@ -72,7 +72,7 @@ <div id="screenshot-container" class="checkbox-field-container"> <input id="screenshot-checkbox" type="checkbox" aria-labelledby="screenshot-label"> <label id="screenshot-label" i18n-content="screenshot"></label> - <img id="screenshot-image" alt="screenshot"> + <img id="screenshot-image"> </div> <!-- System Information --> <div class="checkbox-field-container"> diff --git a/chromium/chrome/browser/resources/feedback/js/feedback.js b/chromium/chrome/browser/resources/feedback/js/feedback.js index 76f6e26ab49..62712dab768 100644 --- a/chromium/chrome/browser/resources/feedback/js/feedback.js +++ b/chromium/chrome/browser/resources/feedback/js/feedback.js @@ -348,7 +348,11 @@ function initialize() { chrome.app.window.current().show(); var screenshotDataUrl = screenshotCanvas.toDataURL('image/png'); + + // Only set the alt text when the src url is available, otherwise we'd + // get a broken image picture instead. crbug.com/773985. $('screenshot-image').src = screenshotDataUrl; + $('screenshot-image').alt = 'screenshot'; $('screenshot-image') .classList.toggle( 'wide-screen', diff --git a/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js b/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js index 878224e9f32..4d1bc38391f 100644 --- a/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js +++ b/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js @@ -73,7 +73,6 @@ cr.define('cr.login', function() { 'constrained', // Whether the extension is loaded in a constrained // window. 'clientId', // Chrome client id. - 'useEafe', // Whether to use EAFE. 'needPassword', // Whether the host is interested in getting a password. // If this set to |false|, |confirmPasswordCallback| is // not called before dispatching |authCopleted|. @@ -144,7 +143,6 @@ cr.define('cr.login', function() { this.newGapsCookie_ = null; this.readyFired_ = false; - this.useEafe_ = false; this.clientId_ = null; this.samlHandler_ = new cr.login.SamlHandler(this.webview_); @@ -168,7 +166,6 @@ cr.define('cr.login', function() { this.webview_.addEventListener( 'contentload', this.onContentLoad_.bind(this)); this.webview_.addEventListener('loadabort', this.onLoadAbort_.bind(this)); - this.webview_.addEventListener('loadstop', this.onLoadStop_.bind(this)); this.webview_.addEventListener('loadcommit', this.onLoadCommit_.bind(this)); this.webview_.request.onCompleted.addListener( this.onRequestCompleted_.bind(this), @@ -232,7 +229,6 @@ cr.define('cr.login', function() { this.continueUrl_; this.isConstrainedWindow_ = data.constrained == '1'; this.isNewGaiaFlow = data.isNewGaiaFlow; - this.useEafe_ = data.useEafe || false; this.clientId_ = data.clientId; this.gapsCookie_ = data.gapsCookie; this.gapsCookieSent_ = false; @@ -562,17 +558,6 @@ cr.define('cr.login', function() { return false; } - // EAFE passes back auth code via message. - if (this.useEafe_ && typeof e.data == 'object' && - e.data.hasOwnProperty('authorizationCode')) { - assert(!this.oauthCode_); - this.oauthCode_ = e.data.authorizationCode; - this.dispatchEvent(new CustomEvent( - 'authCompleted', - {detail: {authCodeOnly: true, authCode: this.oauthCode_}})); - return; - } - // Gaia messages must be an object with 'method' property. if (typeof e.data != 'object' || !e.data.hasOwnProperty('method')) { return false; @@ -846,27 +831,6 @@ cr.define('cr.login', function() { }; /** - * Invoked when the webview finishes loading a page. - * @private - */ - Authenticator.prototype.onLoadStop_ = function(e) { - // Sends client id to EAFE on every loadstop after a small timeout. This is - // needed because EAFE sits behind SSO and initialize asynchrounouly - // and we don't know for sure when it is loaded and ready to listen - // for message. The postMessage is guarded by EAFE's origin. - if (this.useEafe_) { - // An arbitrary small timeout for delivering the initial message. - var EAFE_INITIAL_MESSAGE_DELAY_IN_MS = 500; - window.setTimeout( - (function() { - var msg = {'clientId': this.clientId_}; - this.webview_.contentWindow.postMessage(msg, this.idpOrigin_); - }).bind(this), - EAFE_INITIAL_MESSAGE_DELAY_IN_MS); - } - }; - - /** * Invoked when the webview navigates withing the current document. * @private */ diff --git a/chromium/chrome/browser/resources/instant/instant.css b/chromium/chrome/browser/resources/instant/instant.css index af57b887194..795df5d309d 100644 --- a/chromium/chrome/browser/resources/instant/instant.css +++ b/chromium/chrome/browser/resources/instant/instant.css @@ -9,38 +9,7 @@ body { padding-bottom: 65px; } -img { - float: left; - height: 16px; - padding-right: 5px; - width: 16px; -} - -.section { - background-color: rgb(235, 239, 249); - border-top: 1px solid rgb(181, 199, 222); - font-weight: bold; - margin: 10px 0 0; - padding: 2px 2px; -} - .row { border-bottom: 1px solid #a0a0a0; padding: 5px; } - -.url { - color: #a0a0a0; -} - -.debug { - margin: 1px; -} - -.debug span+span { - padding-left: 5px; -} - -.timestamp { - color: blue; -} diff --git a/chromium/chrome/browser/resources/instant/instant.html b/chromium/chrome/browser/resources/instant/instant.html index 0482eca02db..80fdc497c14 100644 --- a/chromium/chrome/browser/resources/instant/instant.html +++ b/chromium/chrome/browser/resources/instant/instant.html @@ -20,10 +20,5 @@ found in the LICENSE file. <button id="save-button">Save</button> </div> <hr/> - <div> - <h2>Instant Event Log</h2> - <button id="clear-button">Clear</button><br><br> - <div id="instant-debug-info"></div> - </div> </body> </html> diff --git a/chromium/chrome/browser/resources/instant/instant.js b/chromium/chrome/browser/resources/instant/instant.js index 2cb4b971bf9..be590061c23 100644 --- a/chromium/chrome/browser/resources/instant/instant.js +++ b/chromium/chrome/browser/resources/instant/instant.js @@ -5,8 +5,8 @@ // Redefine '$' here rather than including 'cr.js', since this is // the only function needed. This allows this file to be loaded // in a browser directly for layout and some testing purposes. -// eslint-disable-next-line no-restricted-properties var $ = function(id) { + // eslint-disable-next-line no-restricted-properties return document.getElementById(id); }; @@ -138,37 +138,6 @@ var instantConfig = (function() { return false; } - /** - * Request debug info. - * The method is asynchronous, results being provided via getDebugInfoResult. - */ - function getDebugInfo() { - chrome.send('getDebugInfo'); - } - - /** - * Handles callback from getDebugInfo. - * @param {Object} info The debug info. - */ - function getDebugInfoResult(info) { - for (var i = 0; i < info.entries.length; ++i) { - var entry = info.entries[i]; - var row = createElementWithClass('p', 'debug'); - row.appendChild(createElementWithClass('span', 'timestamp')).textContent = - entry.time; - row.appendChild(document.createElement('span')).textContent = entry.text; - $('instant-debug-info').appendChild(row); - } - } - - /** - * Resets list of debug events. - */ - function clearDebugInfo() { - $('instant-debug-info').innerHTML = ''; - chrome.send('clearDebugInfo'); - } - function loadForm() { for (var i = 0; i < FIELDS.length; i++) getPreferenceValue(FIELDS[i].key); @@ -181,15 +150,12 @@ var instantConfig = (function() { buildForm(); loadForm(); initForm(); - getDebugInfo(); $('save-button').onclick = onSave.bind(this); - $('clear-button').onclick = clearDebugInfo.bind(this); } return { initialize: initialize, - getDebugInfoResult: getDebugInfoResult, getPreferenceValueResult: getPreferenceValueResult }; })(); diff --git a/chromium/chrome/browser/resources/interventions_internals/index.css b/chromium/chrome/browser/resources/interventions_internals/index.css new file mode 100644 index 00000000000..4ee2351451c --- /dev/null +++ b/chromium/chrome/browser/resources/interventions_internals/index.css @@ -0,0 +1,58 @@ +/** + * 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. + */ + +.hidden-tab { + display: none; + padding: 12px; +} + +.selected-tab { + display: block; + padding: 12px; +} + +.tab-select input[type=radio] { + display: none; +} + +.tab-select label { + background: #ddd; + border: outset 1px silver; + cursor: pointer; + margin-right: -5px; + padding: 5px 5px; +} + +.tab-select input:checked + span { + background: #aaa; + border: inset 1px silver; +} + +table { + border: 1px solid black; + border-collapse: collapse; +} + +th { + border: 1px solid black; + padding: 15px; + text-align: left; +} + +td { + border: 1px solid black; + padding: 15px; + text-align: left; +} + +.error-header { + font-size: 150%; + font-weight: bold; +} + +.error-message { + font-style: italic; +} diff --git a/chromium/chrome/browser/resources/interventions_internals/index.html b/chromium/chrome/browser/resources/interventions_internals/index.html new file mode 100644 index 00000000000..4de45b78f77 --- /dev/null +++ b/chromium/chrome/browser/resources/interventions_internals/index.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width"> + <title>Interventions-Internals</title> + <script src="chrome://resources/js/cr.js"></script> + <script src="chrome://resources/js/mojo_bindings.js"></script> + <script src="chrome://resources/js/util.js"></script> + <script src="chrome/browser/ui/webui/interventions_internals/interventions_internals.mojom.js"> + </script> + <script src="index.js"></script> + <link rel="stylesheet" type="text/css" href="index.css"> + </head> + + <body> + <nav class="tab-select"> + <label> + <input type="radio" id="statuses" name="tabs" + value="previews-statuses" checked="checked"> + <span>Previews Modes</span> + </label> + <label> + <input type="radio" id="logs" name="tabs" value="message-logs"> + <span>Logs</span> + </label> + </nav> + + <div class="tab-content" id="previews-statuses"> + </div> + + <div class="tab-content" id="message-logs"> + <table id="message-logs-table"> + <tr> + <th id="time-table-header">Time</th> + <th id="type-table-header">Type</th> + <th id="description-table-header">Description</th> + <th id="url-table-header">URL</th> + </tr> + </table> + </div> + </body> +</html> diff --git a/chromium/chrome/browser/resources/interventions_internals/index.js b/chromium/chrome/browser/resources/interventions_internals/index.js new file mode 100644 index 00000000000..b01f1b2bfe8 --- /dev/null +++ b/chromium/chrome/browser/resources/interventions_internals/index.js @@ -0,0 +1,160 @@ +// 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. + +/** + * Switch the selected tab to 'selected-tab' class. + */ +function setSelectedTab() { + let selected = + document.querySelector('input[type=radio][name=tabs]:checked').value; + let selectedTab = document.querySelector('#' + selected); + selectedTab.className = + selectedTab.className.replace('hidden-tab', 'selected-tab'); +} + +/** + * Change the previously selected element to 'hidden-tab' class, and switch the + * selected element to 'selected-tab' class. + */ +function changeTab() { + let lastSelected = document.querySelector('.selected-tab'); + lastSelected.className = + lastSelected.className.replace('selected-tab', 'hidden-tab'); + + setSelectedTab(); +} + +/** + * Initialize the navigation bar, and setup OnChange listeners for the tabs. + */ +function setupTabControl() { + // Initialize on change listeners. + let tabs = document.querySelectorAll('input[type=radio][name=tabs]'); + tabs.forEach((tab) => { + tab.addEventListener('change', changeTab); + }); + + let tabContents = document.querySelectorAll('.tab-content'); + tabContents.forEach((tab) => { + tab.className += ' hidden-tab'; + }); + + // Turn on the default selected tab. + setSelectedTab(); +} + +/** @constructor */ +let InterventionsInternalPageImpl = function(request) { + this.binding_ = + new mojo.Binding(mojom.InterventionsInternalsPage, this, request); +}; + +InterventionsInternalPageImpl.prototype = { + /** + * Post a new log message to the web page. + * + * @override + * @param {!MessageLog} log The new log message recorded by + * PreviewsLogger. + */ + logNewMessage: function(log) { + let logsTable = $('message-logs-table'); + let tableRow = document.createElement('tr'); + tableRow.setAttribute('class', 'log-message'); + + let timeTd = document.createElement('td'); + let date = new Date(log.time); + timeTd.textContent = date.toISOString(); + timeTd.setAttribute('class', 'log-time'); + tableRow.appendChild(timeTd); + + let typeTd = document.createElement('td'); + typeTd.setAttribute('class', 'log-type'); + typeTd.textContent = log.type; + tableRow.appendChild(typeTd); + + let descriptionTd = document.createElement('td'); + descriptionTd.setAttribute('class', 'log-description'); + descriptionTd.textContent = log.description; + tableRow.appendChild(descriptionTd); + + // TODO(thanhdle): Truncate url and show full url when user clicks on it. + // crbug.com/773019 + let urlTd = document.createElement('td'); + urlTd.setAttribute('class', 'log-url'); + urlTd.textContent = log.url.url; + tableRow.appendChild(urlTd); + + logsTable.appendChild(tableRow); + }, +}; + +cr.define('interventions_internals', () => { + let pageHandler = null; + + function init(handler) { + pageHandler = handler; + getPreviewsEnabled(); + } + + /** + * Retrieves the statuses of previews (i.e. Offline, LoFi, AMP Redirection), + * and posts them on chrome://intervention-internals. + */ + function getPreviewsEnabled() { + pageHandler.getPreviewsEnabled() + .then((response) => { + let statuses = $('previews-statuses'); + + // TODO(thanhdle): The statuses are not printed in alphabetic order of + // the key. crbug.com/772458 + response.statuses.forEach((value, key) => { + let message = value.description + ': '; + message += value.enabled ? 'Enabled' : 'Disabled'; + + assert(!$(key), 'Component ' + key + ' already existed!'); + + let node = document.createElement('p'); + node.setAttribute('id', key); + node.textContent = message; + statuses.appendChild(node); + }); + }) + .catch((error) => { + console.error(error.message); + }); + } + + return { + init: init, + }; +}); + +window.setupFn = window.setupFn || function() { + return Promise.resolve(); +}; + +document.addEventListener('DOMContentLoaded', () => { + setupTabControl(); + let pageHandler = null; + let pageImpl = null; + + window.setupFn().then(() => { + if (window.testPageHandler) { + pageHandler = window.testPageHandler; + } else { + pageHandler = new mojom.InterventionsInternalsPageHandlerPtr; + Mojo.bindInterface( + mojom.InterventionsInternalsPageHandler.name, + mojo.makeRequest(pageHandler).handle); + + // Set up client side mojo interface. + let client = new mojom.InterventionsInternalsPagePtr; + pageImpl = new InterventionsInternalPageImpl(mojo.makeRequest(client)); + pageHandler.setClientPage(client); + } + + interventions_internals.init(pageHandler); + }); +}); diff --git a/chromium/chrome/browser/resources/interventions_internals/unsupported_page.html b/chromium/chrome/browser/resources/interventions_internals/unsupported_page.html new file mode 100644 index 00000000000..733210fcbe3 --- /dev/null +++ b/chromium/chrome/browser/resources/interventions_internals/unsupported_page.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width"> + <title>Interventions-Internals</title> + <link rel="stylesheet" type="text/css" href="index.css"> + </head> + + <body> + <div class="error-messages"> + <div class="error-header"> + Not supported. + </div> + <div class="error-message"> + chrome://interventions-internals is not supported in Guest Mode or + Incognito mode. + </div> + </div> + </body> +</html> diff --git a/chromium/chrome/browser/resources/local_ntp/OWNERS b/chromium/chrome/browser/resources/local_ntp/OWNERS index 8d329af858c..9c2e2d6a1f3 100644 --- a/chromium/chrome/browser/resources/local_ntp/OWNERS +++ b/chromium/chrome/browser/resources/local_ntp/OWNERS @@ -1,5 +1,4 @@ jeremycho@chromium.org -samarth@chromium.org mathp@chromium.org fserb@chromium.org huangs@chromium.org diff --git a/chromium/chrome/browser/resources/local_ntp/local_ntp.css b/chromium/chrome/browser/resources/local_ntp/local_ntp.css index ce120114c42..7c507af4ae1 100644 --- a/chromium/chrome/browser/resources/local_ntp/local_ntp.css +++ b/chromium/chrome/browser/resources/local_ntp/local_ntp.css @@ -70,18 +70,34 @@ body.hide-fakebox-logo #fakebox { display: none; } -#logo-default { +#logo-default, +#logo-non-white, +#logo-doodle { + position: absolute; +} + +#logo-default, +#logo-doodle { + opacity: 0; +} + +#logo-default.fade, +#logo-doodle.fade { + transition: opacity 130ms; +} + +#logo-default, +#logo-non-white { background-image: url(../../../../ui/webui/resources/images/google_logo.svg); background-repeat: no-repeat; - bottom: 0; height: 92px; left: calc(50% - 272px/2); - position: absolute; - transition: opacity 130ms; + top: 157px; width: 272px; } -body.alternate-logo #logo-default { +body.alternate-logo #logo-default, +body.alternate-logo #logo-non-white { -webkit-mask-image: url(../../../../ui/webui/resources/images/google_logo.svg); -webkit-mask-repeat: no-repeat; @@ -89,6 +105,106 @@ body.alternate-logo #logo-default { background: #eee; } +#logo-default, +.non-white-bg #logo-non-white { + display: block; +} +#logo-non-white, +.non-white-bg #logo-default { + display: none; +} + +#logo-doodle { + /* Normally, logos are aligned with tiles and bounded to their left and right + * edges. Doodles may be larger than this. In order that they are centered and + * cropped, instead of left-aligned, give them 100px of extra space on the + * left and the right side. + */ + left: -100px; + right: -100px; + top: 44px; +} + +#logo-doodle-link { + cursor: pointer; + margin: 0 auto; +} + +.non-white-bg #logo-doodle-link { + display: none; +} + +#logo-doodle-notifier { + display: none; +} +.non-white-bg #logo-doodle-notifier { + cursor: pointer; + display: inline-block; + height: 24px; + left: 148px; + position: relative; + top: 100px; + width: 24px; +} +@keyframes anim-pos { + 0% { transform: translate(-98%, 0); } + 100% { transform: translate(98%, 0); } +} +@keyframes anim-z-order { + 0% { z-index: 100; } + 100% { z-index: 1; } +} +.non-white-bg #logo-doodle-notifier .outer { + animation: anim-z-order 3520ms linear infinite; + height: 37.5%; + left: 50%; + margin-left: -18.75%; + margin-top: -18.75%; + position: absolute; + top: 50%; + width: 37.5%; +} +.non-white-bg #logo-doodle-notifier .inner { + animation: anim-pos 880ms cubic-bezier(0.445, 0.05, 0.55, 0.95) + infinite alternate; + border-radius: 50%; + height: 100%; + position: absolute; + transform: rotate(90deg); + width: 100%; +} +.non-white-bg #logo-doodle-notifier .ball0 { + animation-delay: 2640ms; + transform: rotate(45deg); +} +.non-white-bg #logo-doodle-notifier .ball1 { + animation-delay: 1760ms; + transform: rotate(135deg); +} +.non-white-bg #logo-doodle-notifier .ball2 { + transform: rotate(225deg); +} +.non-white-bg #logo-doodle-notifier .ball3 { + animation-delay: 880ms; + transform: rotate(315deg); +} +.non-white-bg #logo-doodle-notifier .ball0 .inner { + background: linear-gradient( + 315deg, rgb(0, 85, 221), rgb(0, 119, 255), rgb(0, 119, 255)); +} +.non-white-bg #logo-doodle-notifier .ball1 .inner { + background: linear-gradient( + 225deg, rgb(221, 0, 0), rgb(238, 51, 51), rgb(255, 119, 85)); +} +.non-white-bg #logo-doodle-notifier .ball2 .inner { + background: linear-gradient( + 90deg, rgb(0, 119, 68), rgb(0, 153, 68), rgb(85, 187, 85)); +} +.non-white-bg #logo-doodle-notifier .ball3 .inner { + background: linear-gradient( + 0deg, rgb(255, 170, 51), rgb(255, 204, 0), rgb(255, 221, 102)); +} + #fakebox { background-color: #fff; border-radius: 2px; @@ -168,7 +284,7 @@ html[dir=rtl] #fakebox-cursor { right: 13px; } -#fakebox-speech { +#fakebox-microphone { background: url(googlemic_clr_24px.svg) no-repeat center; background-size: 24px 24px; cursor: pointer; @@ -179,7 +295,7 @@ html[dir=rtl] #fakebox-cursor { width: 17px; } -html[dir=rtl] #fakebox-speech { +html[dir=rtl] #fakebox-microphone { left: 0; right: auto; } diff --git a/chromium/chrome/browser/resources/local_ntp/local_ntp.html b/chromium/chrome/browser/resources/local_ntp/local_ntp.html index 5722d6c1f6c..82ed2ab45ba 100644 --- a/chromium/chrome/browser/resources/local_ntp/local_ntp.html +++ b/chromium/chrome/browser/resources/local_ntp/local_ntp.html @@ -16,6 +16,8 @@ <meta charset="utf-8"> <meta name="google" value="notranslate"> </head> +<!-- Remember to update the test HTML files in chrome/test/data/local_ntp/ + whenever making changes to this file.--> <body> <!-- Container for the OneGoogleBar HTML. --> <div id="one-google" class="hidden"></div> @@ -26,13 +28,28 @@ <div id="logo"> <!-- The logo that is displayed in the absence of a doodle. --> <div id="logo-default" title="Google"></div> + <!-- Logo displayed when theme prevents doodles. Doesn't fade. --> + <div id="logo-non-white" title="Google"></div> + <!-- A doodle, if any: its link and image. --> + <div id="logo-doodle"> + <a id="logo-doodle-link"> + <img id="logo-doodle-image"></img> + </a> + <!-- A spinner, visible on dark-themed NTPs, prompting the doodle --> + <div id="logo-doodle-notifier"> + <div class="outer ball0"><div class="inner"></div></div> + <div class="outer ball1"><div class="inner"></div></div> + <div class="outer ball2"><div class="inner"></div></div> + <div class="outer ball3"><div class="inner"></div></div> + </div> + </div> </div> <div id="fakebox"> <div id="fakebox-text"></div> - <input id="fakebox-input" autocomplete="off" tabIndex="-1" type="url" + <input id="fakebox-input" autocomplete="off" tabindex="-1" type="url" aria-hidden="true"> <div id="fakebox-cursor"></div> - <div id="fakebox-speech" tabIndex="0" hidden></div> + <div id="fakebox-microphone" tabindex="1" hidden></div> </div> </div> <div id="most-visited"> @@ -43,9 +60,9 @@ <span id="mv-msg"></span> <!-- Links in the notification. --> <span id="mv-notice-links"> - <span id="mv-undo" tabIndex="1"></span> - <span id="mv-restore" tabIndex="1"></span> - <div id="mv-notice-x" tabIndex="1"></div> + <span id="mv-undo" tabindex="2"></span> + <span id="mv-restore" tabindex="2"></span> + <div id="mv-notice-x" tabindex="2"></div> </span> </div> </div> diff --git a/chromium/chrome/browser/resources/local_ntp/local_ntp.js b/chromium/chrome/browser/resources/local_ntp/local_ntp.js index 2c3869364ab..cabafe39fd5 100644 --- a/chromium/chrome/browser/resources/local_ntp/local_ntp.js +++ b/chromium/chrome/browser/resources/local_ntp/local_ntp.js @@ -54,11 +54,12 @@ var NTP_DESIGN = { * @const */ var CLASSES = { - ALTERNATE_LOGO: 'alternate-logo', // Shows white logo if required by theme + ALTERNATE_LOGO: 'alternate-logo', // Shows white logo if required by theme DARK: 'dark', DEFAULT_THEME: 'default-theme', DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide', - FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox + FADE: 'fade', // Enables opacity transition on logo and doodle. + FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox // Applies drag focus style to the fakebox FAKEBOX_DRAG_FOCUS: 'fakebox-drag-focused', HIDE_FAKEBOX_AND_LOGO: 'hide-fakebox-logo', @@ -66,6 +67,7 @@ var CLASSES = { LEFT_ALIGN_ATTRIBUTION: 'left-align-attribution', // Vertically centers the most visited section for a non-Google provided page. NON_GOOGLE_PAGE: 'non-google-page', + NON_WHITE_BG: 'non-white-bg', RTL: 'rtl' // Right-to-left language text. }; @@ -82,8 +84,13 @@ var IDS = { FAKEBOX: 'fakebox', FAKEBOX_INPUT: 'fakebox-input', FAKEBOX_TEXT: 'fakebox-text', - FAKEBOX_SPEECH: 'fakebox-speech', + FAKEBOX_MICROPHONE: 'fakebox-microphone', LOGO: 'logo', + LOGO_DEFAULT: 'logo-default', + LOGO_DOODLE: 'logo-doodle', + LOGO_DOODLE_IMAGE: 'logo-doodle-image', + LOGO_DOODLE_LINK: 'logo-doodle-link', + LOGO_DOODLE_NOTIFIER: 'logo-doodle-notifier', NOTIFICATION: 'mv-notice', NOTIFICATION_CLOSE_BUTTON: 'mv-notice-x', NOTIFICATION_MESSAGE: 'mv-msg', @@ -96,6 +103,45 @@ var IDS = { /** + * The different types of events that are logged from the NTP. This enum is + * used to transfer information from the NTP JavaScript to the renderer and is + * not used as a UMA enum histogram's logged value. + * Note: Keep in sync with common/ntp_logging_events.h + * @enum {number} + * @const + */ +var LOG_TYPE = { + // A static Doodle was shown, coming from cache. + NTP_STATIC_LOGO_SHOWN_FROM_CACHE: 30, + // A static Doodle was shown, coming from the network. + NTP_STATIC_LOGO_SHOWN_FRESH: 31, + // A call-to-action Doodle image was shown, coming from cache. + NTP_CTA_LOGO_SHOWN_FROM_CACHE: 32, + // A call-to-action Doodle image was shown, coming from the network. + NTP_CTA_LOGO_SHOWN_FRESH: 33, + + // A static Doodle was clicked. + NTP_STATIC_LOGO_CLICKED: 34, + // A call-to-action Doodle was clicked. + NTP_CTA_LOGO_CLICKED: 35, + // An animated Doodle was clicked. + NTP_ANIMATED_LOGO_CLICKED: 36, + + // The One Google Bar was shown. + NTP_ONE_GOOGLE_BAR_SHOWN: 37, +}; + + +/** + * Background colors considered "white". Used to determine if it is possible + * to display a Google Doodle, or if the notifier should be used instead. + * @type {Array<string>} + * @const + */ +var WHITE_BACKGROUND_COLORS = ['rgba(255,255,255,1)', 'rgba(0,0,0,0)']; + + +/** * Enum for keycodes. * @enum {number} * @const @@ -125,13 +171,36 @@ var MAX_NUM_TILES_TO_SHOW = 8; /** + * Returns theme background info, first checking for history.state.notheme. If + * the page has notheme set, returns a fallback light-colored theme. + */ +function getThemeBackgroundInfo() { + if (history.state && history.state.notheme) { + return { + alternateLogo: false, + backgroundColorRgba: [255, 255, 255, 255], + colorRgba: [255, 255, 255, 255], + headerColorRgba: [150, 150, 150, 255], + linkColorRgba: [6, 55, 116, 255], + sectionBorderColorRgba: [150, 150, 150, 255], + textColorLightRgba: [102, 102, 102, 255], + textColorRgba: [0, 0, 0, 255], + usingDefaultTheme: true, + }; + } + return ntpApiHandle.themeBackgroundInfo; +} + + +/** * Heuristic to determine whether a theme should be considered to be dark, so * the colors of various UI elements can be adjusted. * @param {ThemeBackgroundInfo|undefined} info Theme background information. * @return {boolean} Whether the theme is dark. * @private */ -function getIsThemeDark(info) { +function getIsThemeDark() { + var info = getThemeBackgroundInfo(); if (!info) return false; // Heuristic: light text implies dark theme. @@ -146,8 +215,8 @@ function getIsThemeDark(info) { * @private */ function renderTheme() { - var info = ntpApiHandle.themeBackgroundInfo; - var isThemeDark = getIsThemeDark(info); + var info = getThemeBackgroundInfo(); + var isThemeDark = getIsThemeDark(); $(IDS.NTP_CONTENTS).classList.toggle(CLASSES.DARK, isThemeDark); if (!info) { return; @@ -161,6 +230,8 @@ function renderTheme() { document.body.style.background = background; document.body.classList.toggle(CLASSES.ALTERNATE_LOGO, info.alternateLogo); + var isNonWhiteBackground = !WHITE_BACKGROUND_COLORS.includes(background); + document.body.classList.toggle(CLASSES.NON_WHITE_BG, isNonWhiteBackground); updateThemeAttribution(info.attributionUrl, info.imageHorizontalAlignment); setCustomThemeStyle(info); @@ -194,7 +265,7 @@ function renderOneGoogleBarTheme() { var oneGoogleBarApi = window.gbar.a; var oneGoogleBarPromise = oneGoogleBarApi.bf(); oneGoogleBarPromise.then(function(oneGoogleBar) { - var isThemeDark = getIsThemeDark(ntpApiHandle.themeBackgroundInfo); + var isThemeDark = getIsThemeDark(); var setForegroundStyle = oneGoogleBar.pc.bind(oneGoogleBar); setForegroundStyle(isThemeDark ? 1 : 0); }); @@ -428,7 +499,7 @@ function isFakeboxFocused() { */ function isFakeboxClick(event) { return $(IDS.FAKEBOX).contains(event.target) && - !$(IDS.FAKEBOX_SPEECH).contains(event.target); + !$(IDS.FAKEBOX_MICROPHONE).contains(event.target); } @@ -454,21 +525,32 @@ function registerKeyHandler(element, keycode, handler) { /** - * Event handler for the focus changed and blacklist messages on link elements. - * Used to toggle visual treatment on the tiles (depending on the message). + * Event handler for messages from the most visited iframe. * @param {Event} event Event received. */ function handlePostMessage(event) { var cmd = event.data.cmd; var args = event.data; - if (cmd == 'tileBlacklisted') { + if (cmd == 'loaded') { + if (configData.isGooglePage && !$('one-google-loader')) { + // Load the OneGoogleBar script. It'll create a global variable name "og" + // which is a dict corresponding to the native OneGoogleBarData type. + // We do this only after all the tiles have loaded, to avoid slowing down + // the main page load. + var ogScript = document.createElement('script'); + ogScript.id = 'one-google-loader'; + ogScript.src = 'chrome-search://local-ntp/one-google.js'; + document.body.appendChild(ogScript); + ogScript.onload = function() { + injectOneGoogleBar(og); + }; + } + } else if (cmd == 'tileBlacklisted') { showNotification(); lastBlacklistedTile = args.tid; ntpApiHandle.deleteMostVisitedItem(args.tid); } - // TODO(treib): Should we also handle the 'loaded' message from the iframe - // here? We could hide the page until it arrives, to avoid flicker. } @@ -522,7 +604,7 @@ function init() { if (configData.isVoiceSearchEnabled) { speech.init( configData.googleBaseUrl, configData.translatedStrings, - $(IDS.FAKEBOX_SPEECH), searchboxApiHandle); + $(IDS.FAKEBOX_MICROPHONE), searchboxApiHandle); } // Listener for updating the key capture state. @@ -561,14 +643,39 @@ function init() { // Update the fakebox style to match the current key capturing state. setFakeboxFocus(searchboxApiHandle.isKeyCaptureEnabled); - // Load the OneGoogleBar script. It'll create a global variable name "og" - // which is a dict corresponding to the native OneGoogleBarData type. - var ogScript = document.createElement('script'); - ogScript.src = 'chrome-search://local-ntp/one-google.js'; - document.body.appendChild(ogScript); - ogScript.onload = function() { - injectOneGoogleBar(og); - }; + // Load the Doodle. After the first request completes (getting cached + // data), issue a second request for fresh Doodle data. + loadDoodle(/*v=*/null, function(ddl) { + if (ddl === null) { + // Got no ddl object at all, the feature is probably disabled. Just show + // the logo. + showLogoOrDoodle(null, null, /*fromCache=*/true); + return; + } + + // Got a (possibly empty) ddl object. Show logo or doodle. + showLogoOrDoodle( + ddl.image || null, ddl.metadata || null, /*fromCache=*/true); + // If we got a valid ddl object (from cache), load a fresh one. + if (ddl.v !== null) { + loadDoodle(ddl.v, function(ddl) { + if (ddl.usable) { + fadeToLogoOrDoodle(ddl.image, ddl.metadata); + } + }); + } + }); + + // Set up doodle notifier (but it may be invisible). + var doodleNotifier = $(IDS.LOGO_DOODLE_NOTIFIER); + doodleNotifier.title = configData.translatedStrings.clickToViewDoodle; + doodleNotifier.addEventListener('click', function(e) { + e.preventDefault(); + var state = window.history.state || {}; + state.notheme = true; + window.history.replaceState(state, document.title); + onThemeChange(); + }); } else { document.body.classList.add(CLASSES.NON_GOOGLE_PAGE); } @@ -648,9 +755,186 @@ function injectOneGoogleBar(ogb) { endOfBodyScript.type = 'text/javascript'; endOfBodyScript.appendChild(document.createTextNode(ogb.endOfBodyScript)); document.body.appendChild(endOfBodyScript); + + ntpApiHandle.logEvent(LOG_TYPE.NTP_ONE_GOOGLE_BAR_SHOWN); } +/** Loads the Doodle. On success, the loaded script declares a global variable + * ddl, which onload() receives as its single argument. On failure, onload() is + * called with null as the argument. If v is null, then the call requests a + * cached logo. If non-null, it must be the ddl.v of a previous request for a + * cached logo, and the corresponding fresh logo is returned. + * @param {?number} v + * @param {function(?{v, usable, image, metadata})} onload + */ +var loadDoodle = function(v, onload) { + var ddlScript = document.createElement('script'); + ddlScript.src = 'chrome-search://local-ntp/doodle.js'; + if (v !== null) + ddlScript.src += '?v=' + v; + ddlScript.onload = function() { + onload(ddl); + }; + ddlScript.onerror = function() { + onload(null); + }; + // TODO(treib,sfiera): Add a timeout in case something goes wrong? + document.body.appendChild(ddlScript); +}; + + +/** Returns true if |element| is fully hidden. Returns false if fully visible, + * fading in, or fading out. + * @param {HTMLElement} element + */ +var isFadedOut = function(element) { + return (element.style.opacity == 0) && + (window.getComputedStyle(element).opacity == 0); +}; + + +/** Returns true if the doodle given by |image| and |metadata| is currently + * visible. If |image| is null, returns true when the default logo is visible; + * if non-null, checks that it matches the doodle that is currently visible. + * Here, "visible" means fully-visible or fading in. + * + * @param {?Object} image + * @param {?Object} metadata + * @returns {boolean} + */ +var isDoodleCurrentlyVisible = function(image, metadata) { + var haveDoodle = ($(IDS.LOGO_DOODLE).style.opacity != 0); + var wantDoodle = (image !== null) && (metadata !== null); + if (!haveDoodle || !wantDoodle) + return haveDoodle === wantDoodle; + + // Have a visible doodle and a query doodle. Test that they match. + var logoDoodleImage = $(IDS.LOGO_DOODLE_IMAGE); + return (logoDoodleImage.src === image) || + (logoDoodleImage.src === metadata.animatedUrl); +}; + + +var showLogoOrDoodle = function(image, metadata, fromCache) { + if (metadata !== null) { + applyDoodleMetadata(metadata); + $(IDS.LOGO_DOODLE_IMAGE).src = image; + $(IDS.LOGO_DOODLE).style.opacity = 1; + + var isCta = !!metadata.animatedUrl; + var eventType = isCta ? + (fromCache ? LOG_TYPE.NTP_CTA_LOGO_SHOWN_FROM_CACHE : + LOG_TYPE.NTP_CTA_LOGO_SHOWN_FRESH) : + (fromCache ? LOG_TYPE.NTP_STATIC_LOGO_SHOWN_FROM_CACHE : + LOG_TYPE.NTP_STATIC_LOGO_SHOWN_FRESH); + ntpApiHandle.logEvent(eventType); + } else { + $(IDS.LOGO_DEFAULT).style.opacity = 1; + } +}; + + +/** The image and metadata that should be shown, according to the latest fetch. + * After a logo fades out, onDoodleTransitionEnd fades in a logo according to + * targetDoodle. + */ +var targetDoodle = { + image: null, + metadata: null, +}; + + +/** + * Starts fading out the given element, which should be either the default logo + * or the doodle. + * + * @param {HTMLElement} element + */ +var startFadeOut = function(element) { + // Compute style now, to ensure that the transition from 1 -> 0 is properly + // recognized. Otherwise, if a 0 -> 1 -> 0 transition is too fast, the + // element might stay invisible instead of appearing then fading out. + window.getComputedStyle(element).opacity; + + element.classList.add(CLASSES.FADE); + element.addEventListener('transitionend', onDoodleTransitionEnd); + element.style.opacity = 0; +}; + + +/** + * Integrates a fresh doodle into the page as appropriate. If the correct logo + * or doodle is already shown, just updates the metadata. Otherwise, initiates + * a fade from the currently-shown logo/doodle to the new one. + * + * @param {?Object} image + * @param {?Object} metadata + */ +var fadeToLogoOrDoodle = function(image, metadata) { + // If the image is already visible, there's no need to start a fade-out. + // However, metadata may have changed, so update the doodle's alt text and + // href, if applicable. + if (isDoodleCurrentlyVisible(image, metadata)) { + if (metadata !== null) { + applyDoodleMetadata(metadata); + } + return; + } + + // Set the target to use once the current logo/doodle has finished fading out. + targetDoodle.image = image; + targetDoodle.metadata = metadata; + + // Start fading out the current logo or doodle. onDoodleTransitionEnd will + // apply the change when the fade-out finishes. + startFadeOut($(IDS.LOGO_DEFAULT)); + startFadeOut($(IDS.LOGO_DOODLE)); +}; + + +var onDoodleTransitionEnd = function(e) { + var logoDoodle = $(IDS.LOGO_DOODLE); + var logoDoodleImage = $(IDS.LOGO_DOODLE_IMAGE); + var logoDefault = $(IDS.LOGO_DEFAULT); + + if (isFadedOut(logoDoodle) && isFadedOut(logoDefault)) { + // Fade-out finished. Start fading in the appropriate logo. + showLogoOrDoodle( + targetDoodle.image, targetDoodle.metadata, /*fromCache=*/false); + + logoDefault.removeEventListener('transitionend', onDoodleTransitionEnd); + logoDoodle.removeEventListener('transitionend', onDoodleTransitionEnd); + } +}; + + +var applyDoodleMetadata = function(metadata) { + var logoDoodleLink = $(IDS.LOGO_DOODLE_LINK); + var logoDoodleImage = $(IDS.LOGO_DOODLE_IMAGE); + + logoDoodleImage.title = metadata.altText; + + if (metadata.animatedUrl) { + logoDoodleLink.removeAttribute('href'); + logoDoodleLink.onclick = function(e) { + ntpApiHandle.logEvent(LOG_TYPE.NTP_CTA_LOGO_CLICKED); + e.preventDefault(); + logoDoodleImage.src = metadata.animatedUrl; + logoDoodleLink.href = metadata.onClickUrl; + logoDoodleLink.onclick = function() { + ntpApiHandle.logEvent(LOG_TYPE.NTP_ANIMATED_LOGO_CLICKED); + }; + }; + } else { + logoDoodleLink.href = metadata.onClickUrl; + logoDoodleLink.onclick = function() { + ntpApiHandle.logEvent(LOG_TYPE.NTP_STATIC_LOGO_CLICKED); + }; + } +}; + + return { init: init, // Exposed for testing. listen: listen diff --git a/chromium/chrome/browser/resources/local_ntp/most_visited_single.js b/chromium/chrome/browser/resources/local_ntp/most_visited_single.js index 45e98035dc2..446873a2ce6 100644 --- a/chromium/chrome/browser/resources/local_ntp/most_visited_single.js +++ b/chromium/chrome/browser/resources/local_ntp/most_visited_single.js @@ -27,6 +27,21 @@ var LOG_TYPE = { /** + * The different sources where an NTP tile's title can originate from. + * Note: Keep in sync with components/ntp_tiles/tile_title_source.h + * @enum {number} + * @const + */ +var TileTitleSource = { + UNKNOWN: 0, + MANIFEST: 1, + META_TAG: 2, + TITLE: 3, + INFERRED: 4 +}; + + +/** * The different sources that an NTP tile can have. * Note: Keep in sync with components/ntp_tiles/tile_source.h * @enum {number} @@ -113,23 +128,31 @@ var logEvent = function(eventType) { /** * Log impression of an NTP tile. * @param {number} tileIndex Position of the tile, >= 0 and < NUMBER_OF_TILES. + * @param {number} tileTitleSource The title's source from TileTitleSource. * @param {number} tileSource The source from TileSource. * @param {number} tileType The type from TileVisualType. + * @param {Date} dataGenerationTime Timestamp representing when the tile was + * produced by a ranking algorithm. */ -function logMostVisitedImpression(tileIndex, tileSource, tileType) { +function logMostVisitedImpression( + tileIndex, tileTitleSource, tileSource, tileType, dataGenerationTime) { chrome.embeddedSearch.newTabPage.logMostVisitedImpression( - tileIndex, tileSource, tileType); + tileIndex, tileTitleSource, tileSource, tileType, dataGenerationTime); } /** * Log click on an NTP tile. * @param {number} tileIndex Position of the tile, >= 0 and < NUMBER_OF_TILES. + * @param {number} tileTitleSource The title's source from TileTitleSource. * @param {number} tileSource The source from TileSource. * @param {number} tileType The type from TileVisualType. + * @param {Date} dataGenerationTime Timestamp representing when the tile was + * produced by a ranking algorithm. */ -function logMostVisitedNavigation(tileIndex, tileSource, tileType) { +function logMostVisitedNavigation( + tileIndex, tileTitleSource, tileSource, tileType, dataGenerationTime) { chrome.embeddedSearch.newTabPage.logMostVisitedNavigation( - tileIndex, tileSource, tileType); + tileIndex, tileTitleSource, tileSource, tileType, dataGenerationTime); } /** @@ -305,8 +328,7 @@ var swapInNewTiles = function() { */ var addTile = function(args) { if (isFinite(args.rid)) { - // If a valid number passed in |args.rid|: a local Chrome suggestion. Grab - // the data from the embeddedSearch API. + // An actual suggestion. Grab the data from the embeddedSearch API. var data = chrome.embeddedSearch.newTabPage.getMostVisitedItemData(args.rid); if (!data) @@ -318,15 +340,8 @@ var addTile = function(args) { window.devicePixelRatio + 'x/' + data.renderViewId + '/' + data.tid; } tiles.appendChild(renderTile(data)); - } else if (args.url) { - // If a URL is passed: a server-side suggestion. - args.tileSource = TileSource.SUGGESTIONS_SERVICE; - // check sanity of the arguments - if (/^javascript:/i.test(args.url) || - /^javascript:/i.test(args.thumbnailUrl)) - return; - tiles.appendChild(renderTile(args)); - } else { // an empty tile + } else { + // An empty tile tiles.appendChild(renderTile(null)); } }; @@ -395,7 +410,9 @@ var renderTile = function(data) { tile.title = data.title; tile.addEventListener('click', function(ev) { - logMostVisitedNavigation(position, data.tileSource, tileType); + logMostVisitedNavigation( + position, data.tileTitleSource, data.tileSource, tileType, + data.dataGenerationTime); }); tile.addEventListener('keydown', function(event) { @@ -455,7 +472,9 @@ var renderTile = function(data) { img.addEventListener('load', function(ev) { // Store the type for a potential later navigation. tileType = TileVisualType.THUMBNAIL; - logMostVisitedImpression(position, data.tileSource, tileType); + logMostVisitedImpression( + position, data.tileTitleSource, data.tileSource, tileType, + data.dataGenerationTime); // Note: It's important to call countLoad last, because that might emit the // NTP_ALL_TILES_LOADED event, which must happen after the impression log. countLoad(); @@ -465,7 +484,9 @@ var renderTile = function(data) { thumb.removeChild(img); // Store the type for a potential later navigation. tileType = TileVisualType.THUMBNAIL_FAILED; - logMostVisitedImpression(position, data.tileSource, tileType); + logMostVisitedImpression( + position, data.tileTitleSource, data.tileSource, tileType, + data.dataGenerationTime); // Note: It's important to call countLoad last, because that might emit the // NTP_ALL_TILES_LOADED event, which must happen after the impression log. countLoad(); @@ -473,21 +494,17 @@ var renderTile = function(data) { thumb.appendChild(img); var favicon = tile.querySelector('.mv-favicon'); - if (data.faviconUrl) { - var fi = document.createElement('img'); - fi.src = data.faviconUrl; - // Set the title to empty so screen readers won't say the image name. - fi.title = ''; - loadedCounter += 1; - fi.addEventListener('load', countLoad); - fi.addEventListener('error', countLoad); - fi.addEventListener('error', function(ev) { - favicon.classList.add('failed-favicon'); - }); - favicon.appendChild(fi); - } else { + var fi = document.createElement('img'); + fi.src = data.faviconUrl; + // Set the title to empty so screen readers won't say the image name. + fi.title = ''; + loadedCounter += 1; + fi.addEventListener('load', countLoad); + fi.addEventListener('error', countLoad); + fi.addEventListener('error', function(ev) { favicon.classList.add('failed-favicon'); - } + }); + favicon.appendChild(fi); var mvx = tile.querySelector('.mv-x'); mvx.addEventListener('click', function(ev) { diff --git a/chromium/chrome/browser/resources/local_ntp/most_visited_util.js b/chromium/chrome/browser/resources/local_ntp/most_visited_util.js index 63a6f03056f..12eb39e78d5 100644 --- a/chromium/chrome/browser/resources/local_ntp/most_visited_util.js +++ b/chromium/chrome/browser/resources/local_ntp/most_visited_util.js @@ -225,13 +225,7 @@ function fillMostVisited(location, fill) { domain: params.dom || '' }; } else { - var apiHandle = chrome.embeddedSearch.newTabPage; - // Note: This does not actually result in any logging; it's a workaround for - // crbug.com/698675. It effectively sets the "instant support" state of the - // tab to true, which makes later calls to fetch the most visited items - // succeed. - apiHandle.logEvent(NTP_LOGGING_EVENT_TYPE.NTP_ALL_TILES_RECEIVED); - data = apiHandle.getMostVisitedItemData(params.rid); + data = chrome.embeddedSearch.newTabPage.getMostVisitedItemData(params.rid); if (!data) return; } diff --git a/chromium/chrome/browser/resources/local_ntp/voice.css b/chromium/chrome/browser/resources/local_ntp/voice.css index 37f783b1623..bcd24e9fa0e 100644 --- a/chromium/chrome/browser/resources/local_ntp/voice.css +++ b/chromium/chrome/browser/resources/local_ntp/voice.css @@ -78,6 +78,11 @@ width: 15px; } +html[dir=rtl] .close-button { + left: 0; + right: auto; +} + .close-button:hover { opacity: .8; } @@ -189,6 +194,10 @@ transition: transform 218ms, opacity 218ms ease-in; } +html[dir=rtl] .button-container { + float: left; +} + /* Common styles applied to the button-container. */ .overlay .button-container, .overlay-hidden .button-container { @@ -198,6 +207,12 @@ width: 165px; } +html[dir=rtl] .overlay .button-container, +html[dir=rtl] .overlay-hidden .button-container { + left: -70px; + right: auto; +} + /* Container style when speech recognition is inactive. */ .overlay-hidden .button-container { transform: scale(.1); @@ -216,7 +231,6 @@ /* TEXT */ /* Classes: * - voice-text - Text area style class - * - voice-text-area - Link style class * - voice-text-2l - 2 line style class * - voice-text-3l - 3 line style class * - voice-text-4l - 4 line style class @@ -245,11 +259,20 @@ top 0s linear 218ms; } +html[dir=rtl] .voice-text { + text-align: right; +} + /* Recognition results text hidden in the Full Page UI. */ .overlay-hidden .voice-text { margin-left: 44px; } +html[dir=rtl] .overlay-hidden .voice-text { + margin-left: 0; + margin-right: 44px; +} + /* Styles applied to the text output elements. Common style for the text area * class for the full Page UI. To vertically center the text as longer queries * are wrapped, the 'top' position is specified in em here and below. */ @@ -261,6 +284,12 @@ width: 460px; } +html[dir=rtl] .overlay .voice-text, +html[dir=rtl] .overlay-hidden .voice-text { + left: auto; + right: -44px; +} + /* Common style for when the text areas are made visible. */ .overlay .voice-text { margin-left: 0; @@ -268,6 +297,11 @@ transition: opacity 500ms ease-out, margin-left 500ms ease-out; } +html[dir=rtl] .overlay .voice-text { + margin-left: auto; + margin-right: 0; +} + /* Interim (low confidence) text. */ #voice-text-i { color: var(--grey); @@ -279,7 +313,7 @@ } /* Text area links. */ -#voice-text-area { +.voice-text-link { color: var(--text_link_color); cursor: pointer; font-size: 18px; @@ -350,6 +384,7 @@ /* Container element for microphone icon. */ .microphone { + direction: ltr; height: 87px; left: 43px; pointer-events: none; diff --git a/chromium/chrome/browser/resources/local_ntp/voice.js b/chromium/chrome/browser/resources/local_ntp/voice.js index 3e8623c89d5..1ba0323c44e 100644 --- a/chromium/chrome/browser/resources/local_ntp/voice.js +++ b/chromium/chrome/browser/resources/local_ntp/voice.js @@ -36,20 +36,59 @@ function getChromeUILanguage() { /** - * Enum for keycodes. - * @enum {number} + * The different types of user action and error events that are logged + * from Voice Search. This enum is used to transfer information to + * the renderer and is not used as a UMA enum histogram's logged value. + * Note: Keep in sync with common/ntp_logging_events.h + * @enum {!number} + * @const + */ +const LOG_TYPE = { + // Activated by clicking on the fakebox icon. + ACTION_ACTIVATE_FAKEBOX: 13, + // Activated by keyboard shortcut. + ACTION_ACTIVATE_KEYBOARD: 14, + // Close the voice overlay by a user's explicit action. + ACTION_CLOSE_OVERLAY: 15, + // Submitted voice query. + ACTION_QUERY_SUBMITTED: 16, + // Clicked on support link in error message. + ACTION_SUPPORT_LINK_CLICKED: 17, + // Retried by clicking Try Again link. + ACTION_TRY_AGAIN_LINK: 18, + // Retried by clicking microphone button. + ACTION_TRY_AGAIN_MIC_BUTTON: 10, + // Errors received from the Speech Recognition API. + ERROR_NO_SPEECH: 20, + ERROR_ABORTED: 21, + ERROR_AUDIO_CAPTURE: 22, + ERROR_NETWORK: 23, + ERROR_NOT_ALLOWED: 24, + ERROR_SERVICE_NOT_ALLOWED: 25, + ERROR_BAD_GRAMMAR: 26, + ERROR_LANGUAGE_NOT_SUPPORTED: 27, + ERROR_NO_MATCH: 28, + ERROR_OTHER: 29 +}; + + +/** + * Enum for keyboard event codes. + * @enum {!string} * @const */ const KEYCODE = { - ENTER: 13, - ESC: 27, - PERIOD: 190 + ENTER: 'Enter', + ESC: 'Escape', + NUMPAD_ENTER: 'NumpadEnter', + PERIOD: 'Period', + SPACE: 'Space' }; /** * The set of possible recognition errors. - * @enum {number} + * @enum {!number} * @const */ const RecognitionError = { @@ -85,6 +124,7 @@ let speech = {}; * networkError: string, * noTranslation: string, * noVoice: string, + * otherError: string, * permissionError: string, * ready: string, * tryAgain: string, @@ -100,6 +140,7 @@ speech.messages = { networkError: '', noTranslation: '', noVoice: '', + otherError: '', permissionError: '', ready: '', tryAgain: '', @@ -113,7 +154,7 @@ speech.messages = { * @private */ speech.State_ = { - // Initial state of the controller. Is never re-entered. + // Initial state of the controller. It is never re-entered. // The only state from which the |speech.init()| method can be called. // The UI overlay is hidden, recognition is inactive. UNINITIALIZED: -1, @@ -257,8 +298,17 @@ speech.recognition_; /** + * Log an event from Voice Search. + * @param {!number} eventType Event from |LOG_TYPE|. + */ +speech.logEvent = function(eventType) { + window.chrome.embeddedSearch.newTabPage.logEvent(eventType); +}; + + +/** * Initialize the speech module as part of the local NTP. Adds event handlers - * and shows the fakebox speech microphone icon. + * and shows the fakebox microphone icon. * @param {!string} googleBaseUrl Base URL for sending queries to Search. * @param {!Object} translatedStrings Dictionary of localized string messages. * @param {!HTMLElement} fakeboxMicrophoneElem Fakebox microphone icon element. @@ -275,10 +325,18 @@ speech.init = function( // Initialize event handlers. fakeboxMicrophoneElem.hidden = false; fakeboxMicrophoneElem.title = translatedStrings.fakeboxMicrophoneTooltip; - fakeboxMicrophoneElem.onmouseup = function(event) { + fakeboxMicrophoneElem.onclick = function(event) { // If propagated, closes the overlay (click on the background). event.stopPropagation(); - speech.toggleStartStop(); + speech.logEvent(LOG_TYPE.ACTION_ACTIVATE_FAKEBOX); + speech.start(); + }; + fakeboxMicrophoneElem.onkeydown = function(event) { + if (!event.repeat && speech.isSpaceOrEnter_(event.code) && + speech.currentState_ == speech.State_.READY) { + event.stopPropagation(); + speech.start(); + } }; window.addEventListener('keydown', speech.onKeyDown); if (searchboxApiHandle.onfocuschange) { @@ -297,6 +355,7 @@ speech.init = function( networkError: translatedStrings.networkError, noTranslation: translatedStrings.noTranslation, noVoice: translatedStrings.noVoice, + otherError: translatedStrings.otherError, permissionError: translatedStrings.permissionError, ready: translatedStrings.ready, tryAgain: translatedStrings.tryAgain, @@ -309,27 +368,6 @@ speech.init = function( /** - * Resets the internal state of Voice Search and disables the speech - * recognition interface. Only used for testing. - * @param {HTMLElement} fakeboxMicrophoneElem Fakebox microphone icon element. - * @param {!Object} searchboxApiHandle SearchBox API handle. - * @private - */ -speech.uninit_ = function(fakeboxMicrophoneElem, searchboxApiHandle) { - speech.reset_(); - speech.googleBaseUrl_ = null; - speech.messages = {}; - speech.currentState_ = speech.State_.UNINITIALIZED; - fakeboxMicrophoneElem.hidden = true; - fakeboxMicrophoneElem.title = ''; - fakeboxMicrophoneElem.onmouseup = null; - window.removeEventListener('keydown', speech.onKeyDown); - searchboxApiHandle.onfocuschange = null; - speech.recognition_ = null; -}; - - -/** * Initializes and configures the speech recognition API. * @private */ @@ -338,7 +376,6 @@ speech.initWebkitSpeech_ = function() { speech.recognition_.continuous = false; speech.recognition_.interimResults = true; speech.recognition_.lang = getChromeUILanguage(); - speech.recognition_.maxAlternatives = 4; speech.recognition_.onaudiostart = speech.handleRecognitionAudioStart_; speech.recognition_.onend = speech.handleRecognitionEnd_; speech.recognition_.onerror = speech.handleRecognitionError_; @@ -351,9 +388,8 @@ speech.initWebkitSpeech_ = function() { /** * Sets up the necessary states for voice search and then starts the * speech recognition interface. - * @private */ -speech.start_ = function() { +speech.start = function() { view.show(); speech.resetIdleTimer_(speech.IDLE_TIMEOUT_MS_); @@ -361,14 +397,14 @@ speech.start_ = function() { document.addEventListener( 'webkitvisibilitychange', speech.onVisibilityChange_, false); - if (!speech.isRecognitionInitialized_()) { + // Initialize |speech.recognition_| if it isn't already. + if (!speech.recognition_) { speech.initWebkitSpeech_(); } - // If |speech.start_()| is called too soon after |speech.stop_()| then the + // If |speech.start()| is called too soon after |speech.stop()| then the // recognition interface hasn't yet reset and an error occurs. In this case // we need to hard-reset it and reissue the |recognition_.start()| command. - // TODO(oskopek): Add tests + possibly fix the root cause. try { speech.recognition_.start(); speech.currentState_ = speech.State_.STARTED; @@ -378,8 +414,7 @@ speech.start_ = function() { speech.recognition_.start(); speech.currentState_ = speech.State_.STARTED; } catch (error2) { - speech.currentState_ = speech.State_.STOPPED; - speech.stop_(); + speech.stop(); } } }; @@ -387,9 +422,9 @@ speech.start_ = function() { /** * Hides the overlay and resets the speech state. - * @private */ -speech.stop_ = function() { +speech.stop = function() { + speech.recognition_.abort(); speech.currentState_ = speech.State_.STOPPED; view.hide(); speech.reset_(); @@ -397,16 +432,6 @@ speech.stop_ = function() { /** - * Aborts speech recognition and calls |speech.stop_()|. - * @private - */ -speech.abort_ = function() { - speech.recognition_.abort(); - speech.stop_(); -}; - - -/** * Resets the internal state to the READY state. * @private */ @@ -514,6 +539,38 @@ speech.handleRecognitionResult_ = function(responseEvent) { /** + * Convert a |RecognitionError| to a |LOG_TYPE| error constant, + * for UMA logging. + * @param {RecognitionError} error The received error. + * @private + */ +speech.errorToLogType_ = function(error) { + switch (error) { + case RecognitionError.ABORTED: + return LOG_TYPE.ERROR_ABORTED; + case RecognitionError.AUDIO_CAPTURE: + return LOG_TYPE.ERROR_AUDIO_CAPTURE; + case RecognitionError.BAD_GRAMMAR: + return LOG_TYPE.ERROR_BAD_GRAMMAR; + case RecognitionError.LANGUAGE_NOT_SUPPORTED: + return LOG_TYPE.ERROR_LANGUAGE_NOT_SUPPORTED; + case RecognitionError.NETWORK: + return LOG_TYPE.ERROR_NETWORK; + case RecognitionError.NO_MATCH: + return LOG_TYPE.ERROR_NO_MATCH; + case RecognitionError.NO_SPEECH: + return LOG_TYPE.ERROR_NO_SPEECH; + case RecognitionError.NOT_ALLOWED: + return LOG_TYPE.ERROR_NOT_ALLOWED; + case RecognitionError.SERVICE_NOT_ALLOWED: + return LOG_TYPE.ERROR_SERVICE_NOT_ALLOWED; + default: + return LOG_TYPE.ERROR_OTHER; + } +}; + + +/** * Handles state transition for the controller when an error occurs * during speech recognition. * @param {RecognitionError} error The appropriate error state from @@ -521,6 +578,7 @@ speech.handleRecognitionResult_ = function(responseEvent) { * @private */ speech.onErrorReceived_ = function(error) { + speech.logEvent(speech.errorToLogType_(error)); speech.resetIdleTimer_(speech.IDLE_TIMEOUT_MS_); speech.errorTimeoutMs_ = speech.getRecognitionErrorTimeout_(error); if (error != RecognitionError.ABORTED) { @@ -558,7 +616,6 @@ speech.handleRecognitionOnNoMatch_ = function() { */ speech.handleRecognitionEnd_ = function() { window.clearTimeout(speech.idleTimer_); - window.clearTimeout(speech.permissionTimer_); let error; switch (speech.currentState_) { @@ -589,6 +646,34 @@ speech.handleRecognitionEnd_ = function() { /** + * Determines whether the user's browser is probably running on a Mac. + * @return {boolean} True iff the user's browser is running on a Mac. + * @private + */ +speech.isUserAgentMac_ = function() { + return window.navigator.userAgent.includes('Macintosh'); +}; + + +/** + * Determines, if the given KeyboardEvent |code| is a space or enter key. + * @param {!string} A KeyboardEvent's |code| property. + * @return True, iff the code represents a space or enter key. + * @private + */ +speech.isSpaceOrEnter_ = function(code) { + switch (code) { + case KEYCODE.ENTER: + case KEYCODE.NUMPAD_ENTER: + case KEYCODE.SPACE: + return true; + default: + return false; + } +}; + + +/** * Handles the following keyboard actions. * - <CTRL> + <SHIFT> + <.> starts voice input(<CMD> + <SHIFT> + <.> on mac). * - <ESC> aborts voice input when the recognition interface is active. @@ -596,34 +681,34 @@ speech.handleRecognitionEnd_ = function() { * @param {KeyboardEvent} event The keydown event. */ speech.onKeyDown = function(event) { - function isUserAgentMac(userAgent) { - return userAgent.includes('Macintosh'); - } - - if (!speech.isRecognizing_()) { - const ctrlKeyPressed = event.ctrlKey || - (isUserAgentMac(window.navigator.userAgent) && event.metaKey); + if (speech.isUiDefinitelyHidden_()) { + const ctrlKeyPressed = + event.ctrlKey || (speech.isUserAgentMac_() && event.metaKey); if (speech.currentState_ == speech.State_.READY && - event.keyCode == KEYCODE.PERIOD && event.shiftKey && ctrlKeyPressed) { - speech.toggleStartStop(); + event.code == KEYCODE.PERIOD && event.shiftKey && ctrlKeyPressed) { + speech.logEvent(LOG_TYPE.ACTION_ACTIVATE_KEYBOARD); + speech.start(); } } else { // Ensures that keyboard events are not propagated during voice input. event.stopPropagation(); - if (event.keyCode == KEYCODE.ESC) { - speech.abort_(); - } else if (event.keyCode == KEYCODE.ENTER && speech.finalResult_) { + if (speech.isSpaceOrEnter_(event.code) && speech.finalResult_) { speech.submitFinalResult_(); + } else if ( + speech.isSpaceOrEnter_(event.code) || event.code == KEYCODE.ESC) { + speech.logEvent(LOG_TYPE.ACTION_CLOSE_OVERLAY); + speech.stop(); } } }; /** - * Stops the recognition interface and closes the UI if no interactions occur - * after some time and the interface is still active. This is a safety net in - * case the recognition.onend event doesn't fire, as is sometime the case. If - * a high confidence transcription was received then show the search results. + * Displays the no match error if no interactions occur after some time while + * the interface is active. This is a safety net in case the onend event + * doesn't fire, or the user has persistent noise in the background, and does + * not speak. If a high confidence transcription was received, then this submits + * the search query instead of displaying an error. * @private */ speech.onIdleTimeout_ = function() { @@ -638,7 +723,7 @@ speech.onIdleTimeout_ = function() { case speech.State_.SPEECH_RECEIVED: case speech.State_.RESULT_RECEIVED: case speech.State_.ERROR_RECEIVED: - speech.abort_(); + speech.onErrorReceived_(RecognitionError.NO_MATCH); break; } }; @@ -655,7 +740,7 @@ speech.onVisibilityChange_ = function() { } if (document.webkitHidden) { - speech.abort_(); + speech.stop(); } }; @@ -665,33 +750,46 @@ speech.onVisibilityChange_ = function() { */ speech.onOmniboxFocused = function() { if (!speech.isUiDefinitelyHidden_()) { - speech.abort_(); + speech.logEvent(LOG_TYPE.ACTION_CLOSE_OVERLAY); + speech.stop(); } }; /** + * Change the location of this tab to the new URL. Used for query submission. + * @param {!URL} The URL to navigate to. + * @private + */ +speech.navigateToUrl_ = function(url) { + window.location.href = url.href; +}; + + +/** * Submits the final spoken speech query to perform a search. * @private */ speech.submitFinalResult_ = function() { window.clearTimeout(speech.idleTimer_); - if (!speech.finalResult_) { throw new Error('Submitting empty query.'); } - // Getting |speech.finalResult_| needs to happen before stopping speech. - const encodedQuery = - encodeURIComponent(speech.finalResult_).replace(/%20/g, '+'); - const queryUrl = speech.googleBaseUrl_ + - // Add the actual query. - 'search?q=' + encodedQuery + - // Add a parameter to indicate that this request is a voice search. - '&gs_ivs=1'; - - speech.stop_(); - window.location.href = queryUrl; + const searchParams = new URLSearchParams(); + // Add the encoded query. Getting |speech.finalResult_| needs to happen + // before stopping speech. + searchParams.append('q', speech.finalResult_); + // Add a parameter to indicate that this request is a voice search. + searchParams.append('gs_ivs', 1); + + // Build the query URL. + const queryUrl = new URL('/search', speech.googleBaseUrl_); + queryUrl.search = searchParams; + + speech.logEvent(LOG_TYPE.ACTION_QUERY_SUBMITTED); + speech.stop(); + speech.navigateToUrl_(queryUrl); }; @@ -767,16 +865,26 @@ speech.resetIdleTimer_ = function(duration) { */ speech.resetErrorTimer_ = function(duration) { window.clearTimeout(speech.errorTimer_); - speech.errorTimer_ = window.setTimeout(speech.stop_, duration); + speech.errorTimer_ = window.setTimeout(speech.stop, duration); +}; + + +/** + * Check to see if the speech recognition interface is running, and has + * received any results. + * @return {boolean} True, if the speech recognition interface is running, + * and has received any results. + */ +speech.hasReceivedResults = function() { + return speech.currentState_ == speech.State_.RESULT_RECEIVED; }; /** * Check to see if the speech recognition interface is running. * @return {boolean} True, if the speech recognition interface is running. - * @private */ -speech.isRecognizing_ = function() { +speech.isRecognizing = function() { switch (speech.currentState_) { case speech.State_.STARTED: case speech.State_.AUDIO_RECEIVED: @@ -791,7 +899,7 @@ speech.isRecognizing_ = function() { /** * Check if the controller is in a state where the UI is definitely hidden. * Since we show the UI for a few seconds after we receive an error from the - * API, we need a separate definition to |speech.isRecognizing_()| to indicate + * API, we need a separate definition to |speech.isRecognizing()| to indicate * when the UI is hidden. <strong>Note:</strong> that if this function * returns false, it might not necessarily mean that the UI is visible. * @return {boolean} True if the UI is hidden. @@ -808,53 +916,28 @@ speech.isUiDefinitelyHidden_ = function() { /** - * Check if the Web Speech API is initialized and event functions are set. - * @return {boolean} True if recognition is initialized. - * @private - */ -speech.isRecognitionInitialized_ = function() { - // TODO(oskopek): Do handlers of |recognition_| get reset? Verify and test. - return !!speech.recognition_; -}; - - -/** - * Toggles starting and stopping of speech recognition by the speech tool. - */ -speech.toggleStartStop = function() { - if (speech.currentState_ == speech.State_.READY) { - speech.start_(); - } else { - speech.abort_(); - } -}; - - -/** * Handles click events during speech recognition. * @param {boolean} shouldSubmit True if a query should be submitted. * @param {boolean} shouldRetry True if the interface should be restarted. + * @param {boolean} navigatingAway True if the browser is navigating away + * from the NTP. * @private */ -speech.onClick_ = function(shouldSubmit, shouldRetry) { +speech.onClick_ = function(shouldSubmit, shouldRetry, navigatingAway) { if (speech.finalResult_ && shouldSubmit) { speech.submitFinalResult_(); } else if (speech.currentState_ == speech.State_.STOPPED && shouldRetry) { - speech.restart(); + speech.reset_(); + speech.start(); + } else if (speech.currentState_ == speech.State_.STOPPED && navigatingAway) { + // If the user clicks on a "Learn more" or "Details" support page link + // from an error message, do nothing, and let Chrome navigate to that page. } else { - speech.abort_(); + speech.logEvent(LOG_TYPE.ACTION_CLOSE_OVERLAY); + speech.stop(); } }; -/** - * Restarts voice recognition. Used for the 'Try again' error link. - * @private - */ -speech.restart = function() { - speech.reset_(); - speech.toggleStartStop(); -}; - /* TEXT VIEW */ /** @@ -865,10 +948,24 @@ let text = {}; /** - * ID for the link shown in error output. + * ID for the "Try Again" link shown in error output. * @const */ -text.ERROR_LINK_ID = 'voice-text-area'; +text.RETRY_LINK_ID = 'voice-retry-link'; + + +/** + * ID for the Voice Search support site link shown in error output. + * @const + */ +text.SUPPORT_LINK_ID = 'voice-support-link'; + + +/** + * Class for the links shown in error output. + * @const @private + */ +text.ERROR_LINK_CLASS_ = 'voice-text-link'; /** @@ -927,6 +1024,15 @@ text.LISTENING_TIMEOUT_MS_ = 2000; /** + * Base link target for help regarding voice search. To be appended + * with a locale string for proper target site localization. + * @const @private + */ +text.SUPPORT_LINK_BASE_ = + 'https://support.google.com/chrome/?p=ui_voice_search&hl='; + + +/** * The final / high confidence speech recognition result element. * @private {Element} */ @@ -955,15 +1061,6 @@ text.listeningTimer_; /** - * Base link target for help regarding voice search. To be appended - * with a locale string for proper target site localization. - * @const @private - */ -text.SUPPORT_LINK_BASE_ = - 'https://support.google.com/chrome/?p=ui_voice_search&hl='; - - -/** * Finds the text view elements. */ text.init = function() { @@ -975,39 +1072,38 @@ text.init = function() { /** * Updates the text elements with new recognition results. - * @param {string} interimText Low confidence speech recognition result text. - * @param {string} opt_finalText High confidence speech recognition result text, - * defaults to an empty string. + * @param {!string} interimText Low confidence speech recognition result text. + * @param {!string} opt_finalText High confidence speech recognition result + * text, defaults to an empty string. */ -text.updateTextArea = function(interimText, opt_finalText) { - const finalText = opt_finalText || ''; - +text.updateTextArea = function(interimText, opt_finalText = '') { window.clearTimeout(text.initializingTimer_); - text.cancelListeningTimeout(); + text.clearListeningTimeout(); text.interim_.textContent = interimText; - text.final_.textContent = finalText; + text.final_.textContent = opt_finalText; text.interim_.className = text.final_.className = text.getTextClassName_(); }; /** - * Sets the text view to the initializing state. + * Sets the text view to the initializing state. The initializing message + * shown while waiting for permission is not displayed immediately, but after + * a short timeout. The reason for this is that the "Waiting..." message would + * still appear ("blink") every time a user opens Voice Search, even if they + * have already granted and persisted microphone permission for the NTP, + * and could therefore directly proceed to the "Speak now" message. */ text.showInitializingMessage = function() { + text.interim_.textContent = ''; + text.final_.textContent = ''; + const displayMessage = function() { - if (text.interim_.innerText == '') { + if (text.interim_.textContent == '') { text.updateTextArea(speech.messages.waiting); } }; - - text.interim_.textContent = ''; - text.final_.textContent = ''; - - // We give the interface some time to get the permission. Once permission - // is obtained, the ready message is displayed, in which case the - // initializing message won't be shown. text.initializingTimer_ = window.setTimeout(displayMessage, text.INITIALIZING_TIMEOUT_MS_); }; @@ -1018,12 +1114,14 @@ text.showInitializingMessage = function() { */ text.showReadyMessage = function() { window.clearTimeout(text.initializingTimer_); + text.clearListeningTimeout(); text.updateTextArea(speech.messages.ready); text.startListeningMessageAnimation_(); }; /** + * Display an error message in the text area for the given error. * @param {RecognitionError} error The error that occured. */ text.showErrorMessage = function(error) { @@ -1059,7 +1157,7 @@ text.getErrorMessage_ = function(error) { case RecognitionError.LANGUAGE_NOT_SUPPORTED: return speech.messages.languageError; default: - throw new Error('Illegal RecognitionError value: ' + error); + return speech.messages.otherError; } }; @@ -1071,21 +1169,24 @@ text.getErrorMessage_ = function(error) { */ text.getErrorLink_ = function(error) { let linkElement = document.createElement('a'); - linkElement.id = text.ERROR_LINK_ID; + linkElement.className = text.ERROR_LINK_CLASS_; switch (error) { case RecognitionError.NO_MATCH: + linkElement.id = text.RETRY_LINK_ID; linkElement.textContent = speech.messages.tryAgain; - linkElement.onclick = speech.restart; + // When clicked, |view.onWindowClick_| gets called. return linkElement; case RecognitionError.NO_SPEECH: case RecognitionError.AUDIO_CAPTURE: + linkElement.id = text.SUPPORT_LINK_ID; linkElement.href = text.SUPPORT_LINK_BASE_ + getChromeUILanguage(); linkElement.textContent = speech.messages.learnMore; linkElement.target = '_blank'; return linkElement; case RecognitionError.NOT_ALLOWED: case RecognitionError.SERVICE_NOT_ALLOWED: + linkElement.id = text.SUPPORT_LINK_ID; linkElement.href = text.SUPPORT_LINK_BASE_ + getChromeUILanguage(); linkElement.textContent = speech.messages.details; linkElement.target = '_blank'; @@ -1100,7 +1201,9 @@ text.getErrorLink_ = function(error) { * Clears the text elements. */ text.clear = function() { - text.cancelListeningTimeout(); + text.updateTextArea(''); + + text.clearListeningTimeout(); window.clearTimeout(text.initializingTimer_); text.interim_.className = text.TEXT_AREA_CLASS_; @@ -1111,7 +1214,7 @@ text.clear = function() { /** * Cancels listening message display. */ -text.cancelListeningTimeout = function() { +text.clearListeningTimeout = function() { window.clearTimeout(text.listeningTimer_); }; @@ -1152,7 +1255,8 @@ text.getTextClassName_ = function() { */ text.startListeningMessageAnimation_ = function() { const animateListeningText = function() { - if (text.interim_.innerText == speech.messages.ready) { + // If speech is active with no results yet, show the message and animation. + if (speech.isRecognizing() && !speech.hasReceivedResults()) { text.updateTextArea(speech.messages.listening); text.interim_.classList.add(text.LISTENING_ANIMATION_CLASS_); } @@ -1255,7 +1359,7 @@ microphone.init = function() { /** - * Starts the volume circles animations. + * Starts the volume circles animations, if it has not started yet. */ microphone.startInputAnimation = function() { if (!microphone.isLevelAnimating_) { @@ -1328,13 +1432,6 @@ view.BACKGROUND_ID_ = 'voice-overlay'; /** - * ID of the close (x) button. - * @const @private - */ -view.CLOSE_BUTTON_ID_ = 'voice-close-button'; - - -/** * ID for the speech output container. * @const @private */ @@ -1411,13 +1508,13 @@ view.show = function() { if (!view.isVisible_) { text.showInitializingMessage(); view.showView_(); - window.addEventListener('mouseup', view.onWindowClick_, false); + window.addEventListener('click', view.onWindowClick_, false); } }; /** * Sets the output area text to listening. This should only be called when - * the Web Speech API is receiving audio input (i.e., onaudiostart). + * the Web Speech API starts receiving audio input (i.e., onaudiostart). */ view.setReadyForSpeech = function() { if (view.isVisible_) { @@ -1429,14 +1526,16 @@ view.setReadyForSpeech = function() { /** * Shows the pulsing animation emanating from the microphone. This should only - * be called when the Web Speech API is receiving speech input (i.e., - * onspeechstart). + * be called when the Web Speech API starts receiving speech input (i.e., + * |onspeechstart|). Do note that this may also be run when the Web Speech API + * is receiving speech recognition results (|onresult|), because |onspeechstart| + * may not have been called. */ view.setReceivingSpeech = function() { if (view.isVisible_) { view.container_.className = view.RECEIVING_SPEECH_CLASS_; microphone.startInputAnimation(); - text.cancelListeningTimeout(); + text.clearListeningTimeout(); } }; @@ -1448,7 +1547,11 @@ view.setReceivingSpeech = function() { */ view.updateSpeechResult = function(interimResultText, finalResultText) { if (view.isVisible_) { - view.container_.className = view.RECEIVING_SPEECH_CLASS_; + // If the Web Speech API is receiving speech recognition results + // (|onresult|) and |onspeechstart| has not been called. + if (view.container_.className != view.RECEIVING_SPEECH_CLASS_) { + view.setReceivingSpeech(); + } text.updateTextArea(interimResultText, finalResultText); } }; @@ -1458,7 +1561,7 @@ view.updateSpeechResult = function(interimResultText, finalResultText) { * Hides the UI and stops animations. */ view.hide = function() { - window.removeEventListener('mouseup', view.onWindowClick_, false); + window.removeEventListener('click', view.onWindowClick_, false); view.stopMicrophoneAnimations_(); view.hideView_(); view.isNoMatchShown_ = false; @@ -1543,19 +1646,33 @@ view.stopMicrophoneAnimations_ = function() { /** * Makes sure that a click anywhere closes the UI when it is active. - * @param {Event} event The click event. + * @param {!MouseEvent} event The click event. * @private */ view.onWindowClick_ = function(event) { if (!view.isVisible_) { return; } - const targetId = event.target.id; - const shouldRetry = (targetId == microphone.RED_BUTTON_ID || - targetId == text.ERROR_LINK_ID) && - view.isNoMatchShown_; - const submitQuery = - targetId == microphone.RED_BUTTON_ID && !view.isNoMatchShown_; - view.onClick_(submitQuery, shouldRetry); + const retryLinkClicked = event.target.id === text.RETRY_LINK_ID; + const supportLinkClicked = event.target.id === text.SUPPORT_LINK_ID; + const micIconClicked = event.target.id === microphone.RED_BUTTON_ID; + + const submitQuery = micIconClicked && !view.isNoMatchShown_; + const shouldRetry = + retryLinkClicked || (micIconClicked && view.isNoMatchShown_); + const navigatingAway = supportLinkClicked; + + if (shouldRetry) { + if (micIconClicked) { + speech.logEvent(LOG_TYPE.ACTION_TRY_AGAIN_MIC_BUTTON); + } else if (retryLinkClicked) { + speech.logEvent(LOG_TYPE.ACTION_TRY_AGAIN_LINK); + } + } + if (supportLinkClicked) { + speech.logEvent(LOG_TYPE.ACTION_SUPPORT_LINK_CLICKED); + } + + view.onClick_(submitQuery, shouldRetry, navigatingAway); }; /* END VIEW */ diff --git a/chromium/chrome/browser/resources/local_omnibox_popup/OWNERS b/chromium/chrome/browser/resources/local_omnibox_popup/OWNERS deleted file mode 100644 index 2037852e71e..00000000000 --- a/chromium/chrome/browser/resources/local_omnibox_popup/OWNERS +++ /dev/null @@ -1 +0,0 @@ -samarth@chromium.org diff --git a/chromium/chrome/browser/resources/md_bookmarks/.eslintrc.js b/chromium/chrome/browser/resources/md_bookmarks/.eslintrc.js new file mode 100644 index 00000000000..847d6e99509 --- /dev/null +++ b/chromium/chrome/browser/resources/md_bookmarks/.eslintrc.js @@ -0,0 +1,13 @@ +// 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. + +module.exports = { + 'env': { + 'browser': true, + 'es6': true, + }, + 'rules': { + 'no-var': 'error', + }, +}; diff --git a/chromium/chrome/browser/resources/md_bookmarks/BUILD.gn b/chromium/chrome/browser/resources/md_bookmarks/BUILD.gn index 16e74988bb3..d1d84aac51f 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/BUILD.gn +++ b/chromium/chrome/browser/resources/md_bookmarks/BUILD.gn @@ -1,6 +1,6 @@ -import("../vulcanize.gni") +import("../optimize_webui.gni") -vulcanize("build") { +optimize_webui("build") { host = "bookmarks" html_in_files = [ "bookmarks.html" ] html_out_files = [ "vulcanized.html" ] diff --git a/chromium/chrome/browser/resources/md_bookmarks/actions.js b/chromium/chrome/browser/resources/md_bookmarks/actions.js index 8749fdbf598..9eed3f0e0cf 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/actions.js +++ b/chromium/chrome/browser/resources/md_bookmarks/actions.js @@ -74,7 +74,7 @@ cr.define('bookmarks.actions', function() { * @return {!Action} */ function removeBookmark(id, parentId, index, nodes) { - var descendants = bookmarks.util.getDescendants(nodes, id); + const descendants = bookmarks.util.getDescendants(nodes, id); return { name: 'remove-bookmark', id: id, @@ -156,15 +156,15 @@ cr.define('bookmarks.actions', function() { assert(!config.toggle || !config.range); assert(!config.toggle || !config.clear); - var anchor = state.selection.anchor; - var toSelect = []; - var newAnchor = id; + const anchor = state.selection.anchor; + const toSelect = []; + let newAnchor = id; if (config.range && anchor) { - var displayedList = bookmarks.util.getDisplayedList(state); - var selectedIndex = displayedList.indexOf(id); + const displayedList = bookmarks.util.getDisplayedList(state); + const selectedIndex = displayedList.indexOf(id); assert(selectedIndex != -1); - var anchorIndex = displayedList.indexOf(anchor); + let anchorIndex = displayedList.indexOf(anchor); if (anchorIndex == -1) anchorIndex = selectedIndex; @@ -172,10 +172,10 @@ cr.define('bookmarks.actions', function() { // was used in this selection. newAnchor = displayedList[anchorIndex]; - var startIndex = Math.min(anchorIndex, selectedIndex); - var endIndex = Math.max(anchorIndex, selectedIndex); + const startIndex = Math.min(anchorIndex, selectedIndex); + const endIndex = Math.max(anchorIndex, selectedIndex); - for (var i = startIndex; i <= endIndex; i++) + for (let i = startIndex; i <= endIndex; i++) toSelect.push(displayedList[i]); } else { toSelect.push(id); diff --git a/chromium/chrome/browser/resources/md_bookmarks/api_listener.html b/chromium/chrome/browser/resources/md_bookmarks/api_listener.html index 3fc0a5979ce..0ee48ff8f38 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/api_listener.html +++ b/chromium/chrome/browser/resources/md_bookmarks/api_listener.html @@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://bookmarks/actions.html"> +<link rel="import" href="chrome://bookmarks/debouncer.html"> <link rel="import" href="chrome://bookmarks/store.html"> <link rel="import" href="chrome://bookmarks/util.html"> <script src="chrome://bookmarks/api_listener.js"></script> diff --git a/chromium/chrome/browser/resources/md_bookmarks/api_listener.js b/chromium/chrome/browser/resources/md_bookmarks/api_listener.js index 534e8902aec..a519a86d7d6 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/api_listener.js +++ b/chromium/chrome/browser/resources/md_bookmarks/api_listener.js @@ -10,11 +10,11 @@ cr.define('bookmarks.ApiListener', function() { /** @type {boolean} */ - var trackUpdates = false; + let trackUpdates = false; /** @type {!Array<string>} */ - var updatedItems = []; + let updatedItems = []; - var debouncer; + let debouncer; /** * Batches UI updates so that no changes will be made to UI until the next @@ -22,13 +22,17 @@ cr.define('bookmarks.ApiListener', function() { * can be called in a tight loop by UI actions. */ function batchUIUpdates() { - if (!debouncer || debouncer.done()) { - bookmarks.Store.getInstance().beginBatchUpdate(); + if (!debouncer) { debouncer = new bookmarks.Debouncer( () => bookmarks.Store.getInstance().endBatchUpdate()); } - debouncer.resetTimeout(); + if (debouncer.done()) { + bookmarks.Store.getInstance().beginBatchUpdate(); + debouncer.reset(); + } + + debouncer.restartTimeout(); } /** @@ -91,7 +95,7 @@ cr.define('bookmarks.ApiListener', function() { */ function onBookmarkRemoved(id, removeInfo) { batchUIUpdates(); - var nodes = bookmarks.Store.getInstance().data.nodes; + const nodes = bookmarks.Store.getInstance().data.nodes; dispatch(bookmarks.actions.removeBookmark( id, removeInfo.parentId, removeInfo.index, nodes)); } @@ -152,7 +156,7 @@ cr.define('bookmarks.ApiListener', function() { dispatch(bookmarks.actions.setCanEditBookmarks(canEdit)); } - var listeners = [ + const listeners = [ {api: chrome.bookmarks.onChanged, fn: onBookmarkChanged}, {api: chrome.bookmarks.onChildrenReordered, fn: onChildrenReordered}, {api: chrome.bookmarks.onCreated, fn: onBookmarkCreated}, diff --git a/chromium/chrome/browser/resources/md_bookmarks/app.html b/chromium/chrome/browser/resources/md_bookmarks/app.html index b047f366b8d..68474352701 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/app.html +++ b/chromium/chrome/browser/resources/md_bookmarks/app.html @@ -71,8 +71,8 @@ <bookmarks-toolbar sidebar-width="[[sidebarWidth_]]" role="banner"> </bookmarks-toolbar> <div id="main-container"> - <div id="sidebar" role="navigation"> - <bookmarks-folder-node item-id="0" depth="-1"></bookmarks-folder-node> + <div id="sidebar" role="navigation" aria-label="$i18n{sidebarAxLabel}"> + <bookmarks-folder-node item-id="0" depth="-1"></bookmarks-folder-node> </div> <div id="splitter"></div> <bookmarks-list></bookmarks-list> diff --git a/chromium/chrome/browser/resources/md_bookmarks/app.js b/chromium/chrome/browser/resources/md_bookmarks/app.js index 2622bd0d46e..c95d317ad52 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/app.js +++ b/chromium/chrome/browser/resources/md_bookmarks/app.js @@ -44,11 +44,11 @@ Polymer({ }); chrome.bookmarks.getTree((results) => { - var nodeMap = bookmarks.util.normalizeNodes(results[0]); - var initialState = bookmarks.util.createEmptyState(); + const nodeMap = bookmarks.util.normalizeNodes(results[0]); + const initialState = bookmarks.util.createEmptyState(); initialState.nodes = nodeMap; initialState.selectedFolder = nodeMap[ROOT_NODE_ID].children[0]; - var folderStateString = + const folderStateString = window.localStorage[LOCAL_STORAGE_FOLDER_STATE_KEY]; initialState.folderOpenState = folderStateString ? new Map( @@ -86,9 +86,9 @@ Polymer({ * @private */ initializeSplitter_: function() { - var splitter = this.$.splitter; + const splitter = this.$.splitter; cr.ui.Splitter.decorate(splitter); - var splitterTarget = this.$.sidebar; + const splitterTarget = this.$.sidebar; // The splitter persists the size of the left component in the local store. if (LOCAL_STORAGE_TREE_WIDTH_KEY in window.localStorage) { @@ -120,7 +120,7 @@ Polymer({ return; chrome.bookmarks.search(this.searchTerm_, (results) => { - var ids = results.map(function(node) { + const ids = results.map(function(node) { return node.id; }); this.dispatch(bookmarks.actions.setSearchResults(ids)); diff --git a/chromium/chrome/browser/resources/md_bookmarks/command_manager.js b/chromium/chrome/browser/resources/md_bookmarks/command_manager.js index 486b09231eb..07a7a18051b 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/command_manager.js +++ b/chromium/chrome/browser/resources/md_bookmarks/command_manager.js @@ -8,7 +8,7 @@ */ cr.define('bookmarks', function() { - var CommandManager = Polymer({ + const CommandManager = Polymer({ is: 'bookmarks-command-manager', behaviors: [ @@ -124,7 +124,7 @@ cr.define('bookmarks', function() { this.menuSource_ = source; this.menuIds_ = items || this.getState().selection.items; - var dropdown = + const dropdown = /** @type {!CrActionMenuElement} */ (this.$.dropdown.get()); // Ensure that the menu is fully rendered before trying to position it. Polymer.dom.flush(); @@ -144,7 +144,7 @@ cr.define('bookmarks', function() { this.menuSource_ = source; this.menuIds_ = this.getState().selection.items; - var dropdown = + const dropdown = /** @type {!CrActionMenuElement} */ (this.$.dropdown.get()); // Ensure that the menu is fully rendered before trying to position it. Polymer.dom.flush(); @@ -172,7 +172,7 @@ cr.define('bookmarks', function() { * @return {boolean} */ canExecute: function(command, itemIds) { - var state = this.getState(); + const state = this.getState(); switch (command) { case Command.OPEN: return itemIds.size > 0; @@ -237,7 +237,7 @@ cr.define('bookmarks', function() { switch (command) { case Command.EDIT: case Command.DELETE: - var state = this.getState(); + const state = this.getState(); return !this.containsMatchingNode_(itemIds, function(node) { return !bookmarks.util.canEditNode(state, node.id); }); @@ -258,18 +258,19 @@ cr.define('bookmarks', function() { * @param {!Set<string>} itemIds */ handle: function(command, itemIds) { - var state = this.getState(); + const state = this.getState(); switch (command) { - case Command.EDIT: - var id = Array.from(itemIds)[0]; + case Command.EDIT: { + let id = Array.from(itemIds)[0]; /** @type {!BookmarksEditDialogElement} */ (this.$.editDialog.get()) .showEditDialog(state.nodes[id]); break; + } case Command.COPY_URL: - case Command.COPY: - var idList = Array.from(itemIds); + case Command.COPY: { + let idList = Array.from(itemIds); chrome.bookmarkManagerPrivate.copy(idList, () => { - var labelPromise; + let labelPromise; if (command == Command.COPY_URL) { labelPromise = Promise.resolve(loadTimeData.getString('toastUrlCopied')); @@ -285,17 +286,19 @@ cr.define('bookmarks', function() { labelPromise, state.nodes[idList[0]].title, false); }); break; - case Command.SHOW_IN_FOLDER: - var id = Array.from(itemIds)[0]; + } + case Command.SHOW_IN_FOLDER: { + let id = Array.from(itemIds)[0]; this.dispatch(bookmarks.actions.selectFolder( assert(state.nodes[id].parentId), state.nodes)); bookmarks.DialogFocusManager.getInstance().clearFocus(); this.fire('highlight-items', [id]); break; - case Command.DELETE: - var idList = Array.from(this.minimizeDeletionSet_(itemIds)); - var title = state.nodes[idList[0]].title; - var labelPromise; + } + case Command.DELETE: { + let idList = Array.from(this.minimizeDeletionSet_(itemIds)); + const title = state.nodes[idList[0]].title; + let labelPromise; if (idList.length == 1) { labelPromise = @@ -309,6 +312,7 @@ cr.define('bookmarks', function() { this.showTitleToast_(labelPromise, title, true); }); break; + } case Command.UNDO: chrome.bookmarkManagerPrivate.undo(); bookmarks.ToastManager.getInstance().hide(); @@ -322,12 +326,12 @@ cr.define('bookmarks', function() { this.openUrls_(this.expandUrls_(itemIds), command); break; case Command.OPEN: - var isFolder = itemIds.size == 1 && + const isFolder = itemIds.size == 1 && this.containsMatchingNode_(itemIds, function(node) { return !node.url; }); if (isFolder) { - var folderId = Array.from(itemIds)[0]; + const folderId = Array.from(itemIds)[0]; this.dispatch( bookmarks.actions.selectFolder(folderId, state.nodes)); } else { @@ -335,7 +339,7 @@ cr.define('bookmarks', function() { } break; case Command.SELECT_ALL: - var displayedIds = bookmarks.util.getDisplayedList(state); + const displayedIds = bookmarks.util.getDisplayedList(state); this.dispatch(bookmarks.actions.selectAll(displayedIds, state)); break; case Command.DESELECT_ALL: @@ -345,8 +349,8 @@ cr.define('bookmarks', function() { chrome.bookmarkManagerPrivate.cut(Array.from(itemIds)); break; case Command.PASTE: - var selectedFolder = state.selectedFolder; - var selectedItems = state.selection.items; + const selectedFolder = state.selectedFolder; + const selectedItems = state.selection.items; bookmarks.ApiListener.trackUpdatedItems(); chrome.bookmarkManagerPrivate.paste( selectedFolder, Array.from(selectedItems), @@ -367,9 +371,9 @@ cr.define('bookmarks', function() { * shortcut. */ handleKeyEvent: function(e, itemIds) { - for (var commandTuple of this.shortcuts_) { - var command = /** @type {Command} */ (commandTuple[0]); - var shortcut = + for (const commandTuple of this.shortcuts_) { + const command = /** @type {Command} */ (commandTuple[0]); + const shortcut = /** @type {cr.ui.KeyboardShortcutList} */ (commandTuple[1]); if (shortcut.matchesEvent(e) && this.canExecute(command, itemIds)) { this.handle(command, itemIds); @@ -398,7 +402,7 @@ cr.define('bookmarks', function() { * Mac. */ addShortcut_: function(command, shortcut, macShortcut) { - var shortcut = (cr.isMac && macShortcut) ? macShortcut : shortcut; + shortcut = (cr.isMac && macShortcut) ? macShortcut : shortcut; this.shortcuts_.set(command, new cr.ui.KeyboardShortcutList(shortcut)); }, @@ -412,10 +416,10 @@ cr.define('bookmarks', function() { * @return {!Set<string>} */ minimizeDeletionSet_: function(itemIds) { - var minimizedSet = new Set(); - var nodes = this.getState().nodes; + const minimizedSet = new Set(); + const nodes = this.getState().nodes; itemIds.forEach(function(itemId) { - var currentId = itemId; + let currentId = itemId; while (currentId != ROOT_NODE_ID) { currentId = assert(nodes[currentId].parentId); if (itemIds.has(currentId)) @@ -442,8 +446,8 @@ cr.define('bookmarks', function() { if (urls.length == 0) return; - var openUrlsCallback = function() { - var incognito = command == Command.OPEN_INCOGNITO; + const openUrlsCallback = function() { + const incognito = command == Command.OPEN_INCOGNITO; if (command == Command.OPEN_NEW_WINDOW || incognito) { chrome.windows.create({url: urls, incognito: incognito}); } else { @@ -461,7 +465,7 @@ cr.define('bookmarks', function() { } this.confirmOpenCallback_ = openUrlsCallback; - var dialog = this.$.openDialog.get(); + const dialog = this.$.openDialog.get(); dialog.querySelector('[slot=body]').textContent = loadTimeData.getStringF('openDialogBody', urls.length); @@ -479,16 +483,16 @@ cr.define('bookmarks', function() { * @private */ expandUrls_: function(itemIds) { - var urls = []; - var nodes = this.getState().nodes; + const urls = []; + const nodes = this.getState().nodes; itemIds.forEach(function(id) { - var node = nodes[id]; + const node = nodes[id]; if (node.url) { urls.push(node.url); } else { node.children.forEach(function(childId) { - var childNode = nodes[childId]; + const childNode = nodes[childId]; if (childNode.url) urls.push(childNode.url); }); @@ -505,7 +509,7 @@ cr.define('bookmarks', function() { * |predicate|. */ containsMatchingNode_: function(itemIds, predicate) { - var nodes = this.getState().nodes; + const nodes = this.getState().nodes; return Array.from(itemIds).some(function(id) { return predicate(nodes[id]); @@ -530,18 +534,18 @@ cr.define('bookmarks', function() { * @private */ getCommandLabel_: function(command) { - var multipleNodes = this.menuIds_.size > 1 || + const multipleNodes = this.menuIds_.size > 1 || this.containsMatchingNode_(this.menuIds_, function(node) { return !node.url; }); - var label; + let label; switch (command) { case Command.EDIT: if (this.menuIds_.size != 1) return ''; - var id = Array.from(this.menuIds_)[0]; - var itemUrl = this.getState().nodes[id].url; + const id = Array.from(this.menuIds_)[0]; + const itemUrl = this.getState().nodes[id].url; label = itemUrl ? 'menuEdit' : 'menuRename'; break; case Command.COPY_URL: @@ -573,13 +577,13 @@ cr.define('bookmarks', function() { * @private */ getCommandSublabel_: function(command) { - var multipleNodes = this.menuIds_.size > 1 || + const multipleNodes = this.menuIds_.size > 1 || this.containsMatchingNode_(this.menuIds_, function(node) { return !node.url; }); switch (command) { case Command.OPEN_NEW_TAB: - var urls = this.expandUrls_(this.menuIds_); + const urls = this.expandUrls_(this.menuIds_); return multipleNodes && urls.length > 0 ? String(urls.length) : ''; default: return ''; @@ -616,12 +620,12 @@ cr.define('bookmarks', function() { */ showTitleToast_: function(labelPromise, title, canUndo) { labelPromise.then(function(label) { - var pieces = loadTimeData.getSubstitutedStringPieces(label, title) - .map(function(p) { - // Make the bookmark name collapsible. - p.collapsible = !!p.arg; - return p; - }); + const pieces = loadTimeData.getSubstitutedStringPieces(label, title) + .map(function(p) { + // Make the bookmark name collapsible. + p.collapsible = !!p.arg; + return p; + }); bookmarks.ToastManager.getInstance().showForStringPieces( pieces, canUndo); @@ -660,7 +664,7 @@ cr.define('bookmarks', function() { * @private */ onKeydown_: function(e) { - var selection = this.getState().selection.items; + const selection = this.getState().selection.items; if (e.target == document.body && !bookmarks.DialogFocusManager.getInstance().hasOpenDialog()) { this.handleKeyEvent(e, selection); diff --git a/chromium/chrome/browser/resources/md_bookmarks/compiled_resources2.gyp b/chromium/chrome/browser/resources/md_bookmarks/compiled_resources2.gyp index 0ce298c44a6..bc674665a23 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/md_bookmarks/compiled_resources2.gyp @@ -19,8 +19,8 @@ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(EXTERNS_GYP):chrome_extensions', 'actions', + 'debouncer', 'store', - 'timer_proxy', 'util', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], @@ -63,6 +63,13 @@ 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'] }, { + 'target_name': 'debouncer', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'dialog_focus_manager', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', @@ -85,10 +92,10 @@ '<(EXTERNS_GYP):bookmark_manager_private', '<(EXTERNS_GYP):metrics_private', 'api_listener', + 'debouncer', 'dnd_chip', 'folder_node', 'store', - 'timer_proxy', 'types', 'util', ], @@ -112,6 +119,7 @@ 'target_name': 'folder_node', 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(EXTERNS_GYP):chrome_extensions', 'actions', 'command_manager', @@ -185,20 +193,10 @@ 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'] }, { - 'target_name': 'timer_proxy', - 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { 'target_name': 'toast_manager', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-a11y-announcer/compiled_resources2.gyp:iron-a11y-announcer-extracted', - '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-button/compiled_resources2.gyp:paper-button-extracted', - 'timer_proxy', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, diff --git a/chromium/chrome/browser/resources/md_bookmarks/constants.js b/chromium/chrome/browser/resources/md_bookmarks/constants.js index 85a83d391b8..bbda71cb0fd 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/constants.js +++ b/chromium/chrome/browser/resources/md_bookmarks/constants.js @@ -8,7 +8,7 @@ * @enum {number} * @const */ -var DropPosition = { +const DropPosition = { NONE: 0, ABOVE: 1, ON: 2, @@ -22,7 +22,7 @@ var DropPosition = { * @enum {number} * @const */ -var Command = { +const Command = { EDIT: 0, COPY_URL: 1, SHOW_IN_FOLDER: 2, @@ -47,7 +47,7 @@ var Command = { * @enum {number} * @const */ -var MenuSource = { +const MenuSource = { NONE: 0, LIST: 1, TREE: 2, @@ -58,30 +58,30 @@ var MenuSource = { * @enum {number} * @const */ -var IncognitoAvailability = { +const IncognitoAvailability = { ENABLED: 0, DISABLED: 1, FORCED: 2, }; /** @const */ -var LOCAL_STORAGE_FOLDER_STATE_KEY = 'folderOpenState'; +const LOCAL_STORAGE_FOLDER_STATE_KEY = 'folderOpenState'; /** @const */ -var LOCAL_STORAGE_TREE_WIDTH_KEY = 'treeWidth'; +const LOCAL_STORAGE_TREE_WIDTH_KEY = 'treeWidth'; /** @const */ -var ROOT_NODE_ID = '0'; +const ROOT_NODE_ID = '0'; /** @const */ -var BOOKMARKS_BAR_ID = '1'; +const BOOKMARKS_BAR_ID = '1'; /** @const {number} */ -var OPEN_CONFIRMATION_LIMIT = 15; +const OPEN_CONFIRMATION_LIMIT = 15; /** * Folders that are beneath this depth will be closed by default in the folder * tree (where the Bookmarks Bar folder is at depth 0). * @const {number} */ -var FOLDER_OPEN_BY_DEFAULT_DEPTH = 1; +const FOLDER_OPEN_BY_DEFAULT_DEPTH = 1; diff --git a/chromium/chrome/browser/resources/md_bookmarks/timer_proxy.html b/chromium/chrome/browser/resources/md_bookmarks/debouncer.html index d70600af6a1..567579dff15 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/timer_proxy.html +++ b/chromium/chrome/browser/resources/md_bookmarks/debouncer.html @@ -1,2 +1,2 @@ <link rel="import" href="chrome://resources/html/cr.html"> -<script src="chrome://bookmarks/timer_proxy.js"></script> +<script src="chrome://bookmarks/debouncer.js"></script> diff --git a/chromium/chrome/browser/resources/md_bookmarks/debouncer.js b/chromium/chrome/browser/resources/md_bookmarks/debouncer.js new file mode 100644 index 00000000000..3bc7464a933 --- /dev/null +++ b/chromium/chrome/browser/resources/md_bookmarks/debouncer.js @@ -0,0 +1,86 @@ +// 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. + +/** + * @fileoverview A debouncer which fires the given callback after a delay. The + * delay can be refreshed by calling restartTimeout. Resetting the timeout with + * no delay moves the callback to the end of the task queue. + */ + +cr.define('bookmarks', function() { + class Debouncer { + /** @param {!function()} callback */ + constructor(callback) { + /** @private {!function()} */ + this.callback_ = callback; + /** @private {!Object} */ + this.timerProxy_ = window; + /** @private {?number} */ + this.timer_ = null; + /** @private {!function()} */ + this.boundTimerCallback_ = this.timerCallback_.bind(this); + /** @private {boolean} */ + this.isDone_ = false; + /** @private {!PromiseResolver} */ + this.promiseResolver_ = new PromiseResolver(); + } + + /** + * Starts the timer for the callback, cancelling the old timer if there is + * one. + * @param {number=} delay + */ + restartTimeout(delay) { + assert(!this.isDone_); + + this.cancelTimeout_(); + this.timer_ = + this.timerProxy_.setTimeout(this.boundTimerCallback_, delay || 0); + } + + /** + * @return {boolean} True if the Debouncer has finished processing. + */ + done() { + return this.isDone_; + } + + /** + * @return {!Promise} Promise which resolves immediately after the callback. + */ + get promise() { + return this.promiseResolver_.promise; + } + + /** + * Resets the debouncer as if it had been newly instantiated. + */ + reset() { + this.isDone_ = false; + this.promiseResolver_ = new PromiseResolver(); + this.cancelTimeout_(); + } + + /** + * Cancel the timer callback, which can be restarted by calling + * restartTimeout(). + * @private + */ + cancelTimeout_() { + if (this.timer_) + this.timerProxy_.clearTimeout(this.timer_); + } + + /** @private */ + timerCallback_() { + this.isDone_ = true; + this.callback_.call(); + this.promiseResolver_.resolve(); + } + } + + return { + Debouncer: Debouncer, + }; +}); diff --git a/chromium/chrome/browser/resources/md_bookmarks/dialog_focus_manager.js b/chromium/chrome/browser/resources/md_bookmarks/dialog_focus_manager.js index 68e73b2bda4..79c98298f15 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/dialog_focus_manager.js +++ b/chromium/chrome/browser/resources/md_bookmarks/dialog_focus_manager.js @@ -69,7 +69,7 @@ cr.define('bookmarks', function() { * @private */ getFocusedElement_: function() { - var focus = document.activeElement; + let focus = document.activeElement; while (focus.root && focus.root.activeElement) focus = focus.root.activeElement; @@ -82,7 +82,7 @@ cr.define('bookmarks', function() { * @private */ getCloseListener_: function(dialog) { - var closeListener = (e) => { + const closeListener = (e) => { // If the dialog is open, then it got reshown immediately and we // shouldn't clear it until it is closed again. if (dialog.open) diff --git a/chromium/chrome/browser/resources/md_bookmarks/dnd_chip.js b/chromium/chrome/browser/resources/md_bookmarks/dnd_chip.js index a4108e320d4..22e3b63f9a3 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/dnd_chip.js +++ b/chromium/chrome/browser/resources/md_bookmarks/dnd_chip.js @@ -29,7 +29,7 @@ Polymer({ if (this.showing_) return; - var isFolder = !dragItem.url; + const isFolder = !dragItem.url; this.isMultiItem_ = items.length > 1; this.$.icon.className = isFolder ? 'folder-icon' : 'website-icon'; diff --git a/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.html b/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.html index dc6db92bf87..0a8b34b82f3 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.html +++ b/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.html @@ -1,4 +1,4 @@ <link rel="import" href="chrome://bookmarks/constants.html"> +<link rel="import" href="chrome://bookmarks/debouncer.html"> <link rel="import" href="chrome://bookmarks/dnd_chip.html"> -<link rel="import" href="chrome://bookmarks/timer_proxy.html"> <script src="chrome://bookmarks/dnd_manager.js"></script> diff --git a/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.js b/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.js index 98a4d5ab445..f4da5779d79 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.js +++ b/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.js @@ -4,11 +4,11 @@ /** @typedef {?{elements: !Array<BookmarkNode>, sameProfile: boolean}} */ -var NormalizedDragData; +let NormalizedDragData; cr.define('bookmarks', function() { /** @const {number} */ - var DRAG_THRESHOLD = 15; + const DRAG_THRESHOLD = 15; /** * @param {BookmarkElement} element @@ -51,7 +51,7 @@ cr.define('bookmarks', function() { if (!path) return null; - for (var i = 0; i < path.length; i++) { + for (let i = 0; i < path.length; i++) { if (isBookmarkItem(path[i]) || isBookmarkFolderNode(path[i]) || isBookmarkList(path[i])) { return path[i]; @@ -65,8 +65,8 @@ cr.define('bookmarks', function() { * @return {BookmarkElement} */ function getDragElement(path) { - var dragElement = getBookmarkElement(path); - for (var i = 0; i < path.length; i++) { + const dragElement = getBookmarkElement(path); + for (let i = 0; i < path.length; i++) { if (path[i].tagName == 'BUTTON') return null; } @@ -144,8 +144,8 @@ cr.define('bookmarks', function() { if (!this.isSameProfile()) return false; - var parentId = nodes[itemId].parentId; - var parents = {}; + let parentId = nodes[itemId].parentId; + const parents = {}; while (parentId) { parents[parentId] = true; parentId = nodes[parentId].parentId; @@ -166,14 +166,16 @@ cr.define('bookmarks', function() { /** @const {number} */ this.EXPAND_FOLDER_DELAY = 400; - /** @private {number} */ - this.lastTimestamp_ = 0; - - /** @private {BookmarkElement|null} */ + /** @private {?BookmarkElement} */ this.lastElement_ = null; - /** @private {number} */ - this.testTimestamp_ = 0; + /** @type {!bookmarks.Debouncer} */ + this.debouncer_ = new bookmarks.Debouncer(() => { + const store = bookmarks.Store.getInstance(); + store.dispatch( + bookmarks.actions.changeFolderOpen(this.lastElement_.itemId, true)); + this.reset(); + }); } AutoExpander.prototype = { @@ -182,36 +184,85 @@ cr.define('bookmarks', function() { * @param {?BookmarkElement} overElement */ update: function(e, overElement) { - var eventTimestamp = this.testTimestamp_ || e.timeStamp; - var itemId = overElement ? overElement.itemId : null; - var store = bookmarks.Store.getInstance(); + const itemId = overElement ? overElement.itemId : null; + const store = bookmarks.Store.getInstance(); - // If hovering over the same folder as last update, open the folder after - // the delay has passed. - if (overElement && overElement == this.lastElement_) { - if (eventTimestamp - this.lastTimestamp_ < this.EXPAND_FOLDER_DELAY) - return; - - var action = bookmarks.actions.changeFolderOpen(itemId, true); - store.dispatch(action); - } else if ( - overElement && isClosedBookmarkFolderNode(overElement) && + // If dragging over a new closed folder node with children reset the + // expander. Falls through to reset the expander delay. + if (overElement && overElement != this.lastElement_ && + isClosedBookmarkFolderNode(overElement) && bookmarks.util.hasChildFolders(itemId, store.data.nodes)) { - // Since this is a closed folder node that has children, set the auto - // expander to this element. - this.lastTimestamp_ = eventTimestamp; + this.reset(); this.lastElement_ = overElement; + } + + // If dragging over the same node, reset the expander delay. + if (overElement && overElement == this.lastElement_) { + this.debouncer_.restartTimeout(this.EXPAND_FOLDER_DELAY); return; } - // If the folder has been expanded or we have moved to a different - // element, reset the auto expander. - this.lastTimestamp_ = 0; + // Otherwise, cancel the expander. + this.reset(); + }, + + reset: function() { + this.debouncer_.reset(); this.lastElement_ = null; }, }; /** + * Manages auto scrolling of elements on hover during internal drags. Native + * drags do this by themselves. + * @constructor + */ + function AutoScroller() { + /** @const {number} */ + this.SCROLL_ZONE_LENGTH = 20; + /** @const {number} */ + this.SCROLL_DISTANCE = 10; + /** @const {number} */ + this.SCROLL_INTERVAL = 100; + /** @private {?number} */ + this.intervalId_ = null; + } + + AutoScroller.prototype = { + /** @param {!Event} e */ + update: function(e) { + this.reset(); + + const scrollParent = e.path.find((el) => { + return el.nodeType == Node.ELEMENT_NODE && + window.getComputedStyle(el).overflowY == 'auto'; + }); + + if (!scrollParent) + return; + + const rect = scrollParent.getBoundingClientRect(); + let yDelta = 0; + if (e.clientY < rect.top + this.SCROLL_ZONE_LENGTH) + yDelta = -this.SCROLL_DISTANCE; + else if (e.clientY > rect.bottom - this.SCROLL_ZONE_LENGTH) + yDelta = this.SCROLL_DISTANCE; + + this.intervalId_ = window.setInterval(() => { + scrollParent.scrollTop += yDelta; + }, this.SCROLL_INTERVAL); + }, + + reset: function() { + if (this.intervalId_ == null) + return; + + window.clearInterval(this.intervalId_); + this.intervalId_ = null; + }, + }; + + /** * Encapsulates the behavior of the drag and drop indicator which puts a line * between items or highlights folders which are valid drop targets. * @constructor @@ -237,9 +288,9 @@ cr.define('bookmarks', function() { /** * Used to instantly remove the indicator style in tests. - * @private {bookmarks.TimerProxy} + * @private {!Object} */ - this.timerProxy = new bookmarks.TimerProxy(); + this.timerProxy = window; } DropIndicator.prototype = { @@ -250,7 +301,7 @@ cr.define('bookmarks', function() { * @param {DropPosition} position */ addDropIndicatorStyle: function(indicatorElement, position) { - var indicatorStyleName = position == DropPosition.ABOVE ? + const indicatorStyleName = position == DropPosition.ABOVE ? 'drag-above' : position == DropPosition.BELOW ? 'drag-below' : 'drag-on'; @@ -280,8 +331,8 @@ cr.define('bookmarks', function() { update: function(dropDest) { this.timerProxy.clearTimeout(this.removeDropIndicatorTimeoutId_); - var indicatorElement = dropDest.element.getDropTarget(); - var position = dropDest.position; + const indicatorElement = dropDest.element.getDropTarget(); + const position = dropDest.position; this.removeDropIndicatorStyle(); this.addDropIndicatorStyle(indicatorElement, position); @@ -334,11 +385,17 @@ cr.define('bookmarks', function() { /** @private {Object<string, function(!Event)>} */ this.documentListeners_ = null; + /** @private {?bookmarks.AutoScroller} */ + this.autoScroller_ = null; + + /** @private {?bookmarks.AutoExpander} */ + this.autoExpander_ = null; + /** * Used to instantly clearDragData in tests. - * @private {bookmarks.TimerProxy} + * @private {!Object} */ - this.timerProxy_ = new bookmarks.TimerProxy(); + this.timerProxy_ = window; /** * The bookmark drag and drop indicator chip. @@ -365,6 +422,7 @@ cr.define('bookmarks', function() { this.dragInfo_ = new DragInfo(); this.dropIndicator_ = new DropIndicator(); this.autoExpander_ = new AutoExpander(); + this.autoScroller_ = new AutoScroller(); this.documentListeners_ = { 'mousedown': this.onMousedown_.bind(this), @@ -380,7 +438,7 @@ cr.define('bookmarks', function() { 'dragend': this.clearDragData_.bind(this), // TODO(calamity): Add touch support. }; - for (var event in this.documentListeners_) + for (const event in this.documentListeners_) document.addEventListener(event, this.documentListeners_[event]); chrome.bookmarkManagerPrivate.onDragEnter.addListener( @@ -395,7 +453,7 @@ cr.define('bookmarks', function() { if (this.chip_ && this.chip_.parentElement) document.body.removeChild(this.chip_); - for (var event in this.documentListeners_) + for (const event in this.documentListeners_) document.removeEventListener(event, this.documentListeners_[event]); }, @@ -407,7 +465,7 @@ cr.define('bookmarks', function() { * @param {Event} e */ onMousedown_: function(e) { - var dragElement = getDragElement(e.path); + const dragElement = getDragElement(e.path); if (e.button != 0 || !dragElement) return; @@ -437,6 +495,8 @@ cr.define('bookmarks', function() { // Prevents a native drag from starting. e.preventDefault(); + this.autoScroller_.update(e); + // On the first mousemove after a mousedown, calculate the items to drag. // This can't be done in mousedown because the user may be shift-clicking // an item. @@ -447,7 +507,7 @@ cr.define('bookmarks', function() { return; } - var dragData = this.calculateDragData_(); + const dragData = this.calculateDragData_(); if (!dragData) { this.clearDragData_(); return; @@ -456,8 +516,8 @@ cr.define('bookmarks', function() { this.dragInfo_.dragData = dragData; } - var state = bookmarks.Store.getInstance().data; - var items = this.dragInfo_.dragData.elements; + const state = bookmarks.Store.getInstance().data; + const items = this.dragInfo_.dragData.elements; this.dndChip.showForItems( e.clientX, e.clientY, items, this.internalDragElement_ ? @@ -489,15 +549,17 @@ cr.define('bookmarks', function() { if (this.dropDestination_) { // Complete the drag by moving all dragged items to the drop // destination. - var dropInfo = this.calculateDropInfo_(this.dropDestination_); - var shouldHighlight = this.shouldHighlight_(this.dropDestination_); + const dropInfo = this.calculateDropInfo_(this.dropDestination_); + const shouldHighlight = this.shouldHighlight_(this.dropDestination_); - var movePromises = this.dragInfo_.dragData.elements.map((item) => { + const movePromises = this.dragInfo_.dragData.elements.map((item) => { return new Promise((resolve) => { - chrome.bookmarks.move(item.id, { - parentId: dropInfo.parentId, - index: dropInfo.index == -1 ? undefined : dropInfo.index - }, resolve); + chrome.bookmarks.move( + item.id, { + parentId: dropInfo.parentId, + index: dropInfo.index == -1 ? undefined : dropInfo.index + }, + resolve); }); }); @@ -523,7 +585,7 @@ cr.define('bookmarks', function() { onDragStart_: function(e) { // |e| will be for the originally dragged bookmark item which dragstart // was disabled for due to mousemove's preventDefault. - var dragElement = getDragElement(e.path); + const dragElement = getDragElement(e.path); if (!dragElement) return; @@ -536,7 +598,7 @@ cr.define('bookmarks', function() { // If we are dragging a single link, we can do the *Link* effect. // Otherwise, we only allow copy and move. if (e.dataTransfer) { - var draggedNodes = this.dragInfo_.dragData.elements; + const draggedNodes = this.dragInfo_.dragData.elements; e.dataTransfer.effectAllowed = draggedNodes.length == 1 && draggedNodes[0].url ? 'copyLink' : 'copyMove'; @@ -556,9 +618,9 @@ cr.define('bookmarks', function() { if (this.dropDestination_) { e.preventDefault(); - var dropInfo = this.calculateDropInfo_(this.dropDestination_); - var index = dropInfo.index != -1 ? dropInfo.index : undefined; - var shouldHighlight = this.shouldHighlight_(this.dropDestination_); + const dropInfo = this.calculateDropInfo_(this.dropDestination_); + const index = dropInfo.index != -1 ? dropInfo.index : undefined; + const shouldHighlight = this.shouldHighlight_(this.dropDestination_); if (shouldHighlight) bookmarks.ApiListener.trackUpdatedItems(); @@ -623,8 +685,10 @@ cr.define('bookmarks', function() { /** @private */ clearDragData_: function() { this.dndChip.hide(); + this.autoScroller_.reset(); this.internalDragElement_ = null; this.mouseDownPos_ = null; + this.autoExpander_.reset(); // Defer the clearing of the data so that the bookmark manager API's drop // event doesn't clear the drop data before the web drop event has a @@ -642,12 +706,12 @@ cr.define('bookmarks', function() { * @return {boolean} */ startNativeDrag_: function() { - var state = bookmarks.Store.getInstance().data; + const state = bookmarks.Store.getInstance().data; if (!this.dragInfo_.isDragValid()) return false; - var draggedNodes = + const draggedNodes = this.dragInfo_.dragData.elements.map((item) => item.id); // Clear the drag data here so that the chip is hidden. The native drag @@ -666,10 +730,10 @@ cr.define('bookmarks', function() { * @return {boolean} */ onDragOverCommon_: function(e) { - var state = bookmarks.Store.getInstance().data; - var items = this.dragInfo_.dragData.elements; + const state = bookmarks.Store.getInstance().data; + const items = this.dragInfo_.dragData.elements; - var overElement = getBookmarkElement(e.path); + const overElement = getBookmarkElement(e.path); this.autoExpander_.update(e, overElement); if (!overElement) { this.dropIndicator_.finish(); @@ -705,13 +769,13 @@ cr.define('bookmarks', function() { }; } - var node = getBookmarkNode(dropDestination.element); - var position = dropDestination.position; - var index = -1; - var parentId = node.id; + const node = getBookmarkNode(dropDestination.element); + const position = dropDestination.position; + let index = -1; + let parentId = node.id; if (position != DropPosition.ON) { - var state = bookmarks.Store.getInstance().data; + const state = bookmarks.Store.getInstance().data; // Drops between items in the normal list and the sidebar use the drop // destination node's parent. @@ -734,12 +798,12 @@ cr.define('bookmarks', function() { * @private */ calculateDragData_: function() { - var dragId = this.internalDragElement_.itemId; - var store = bookmarks.Store.getInstance(); - var state = store.data; + const dragId = this.internalDragElement_.itemId; + const store = bookmarks.Store.getInstance(); + const state = store.data; // Determine the selected bookmarks. - var draggedNodes = Array.from(state.selection.items); + let draggedNodes = Array.from(state.selection.items); // Change selection to the dragged node if the node is not part of the // existing selection. @@ -757,7 +821,7 @@ cr.define('bookmarks', function() { } // If any node can't be dragged, end the drag. - var anyUnmodifiable = draggedNodes.some( + const anyUnmodifiable = draggedNodes.some( (itemId) => !bookmarks.util.canEditNode(state, itemId)); if (anyUnmodifiable) @@ -780,15 +844,15 @@ cr.define('bookmarks', function() { * position - A |DropPosition| relative to the |element|. */ calculateDropDestination_: function(elementClientY, overElement) { - var validDropPositions = this.calculateValidDropPositions_(overElement); + const validDropPositions = this.calculateValidDropPositions_(overElement); if (validDropPositions == DropPosition.NONE) return null; - var above = validDropPositions & DropPosition.ABOVE; - var below = validDropPositions & DropPosition.BELOW; - var on = validDropPositions & DropPosition.ON; - var rect = overElement.getDropTarget().getBoundingClientRect(); - var yRatio = (elementClientY - rect.top) / rect.height; + const above = validDropPositions & DropPosition.ABOVE; + const below = validDropPositions & DropPosition.BELOW; + const on = validDropPositions & DropPosition.ON; + const rect = overElement.getDropTarget().getBoundingClientRect(); + const yRatio = (elementClientY - rect.top) / rect.height; if (above && (yRatio <= .25 || yRatio <= .5 && (!below || !on))) return {element: overElement, position: DropPosition.ABOVE}; @@ -810,9 +874,9 @@ cr.define('bookmarks', function() { * @return {DropPosition} An bit field enumeration of valid drop locations. */ calculateValidDropPositions_: function(overElement) { - var dragInfo = this.dragInfo_; - var state = bookmarks.Store.getInstance().data; - var itemId = overElement.itemId; + const dragInfo = this.dragInfo_; + const state = bookmarks.Store.getInstance().data; + let itemId = overElement.itemId; // Drags aren't allowed onto the search result list. if ((isBookmarkList(overElement) || isBookmarkItem(overElement)) && @@ -833,7 +897,7 @@ cr.define('bookmarks', function() { return DropPosition.NONE; } - var validDropPositions = this.calculateDropAboveBelow_(overElement); + let validDropPositions = this.calculateDropAboveBelow_(overElement); if (this.canDropOn_(overElement)) validDropPositions |= DropPosition.ON; @@ -846,8 +910,8 @@ cr.define('bookmarks', function() { * @return {DropPosition} */ calculateDropAboveBelow_: function(overElement) { - var dragInfo = this.dragInfo_; - var state = bookmarks.Store.getInstance().data; + const dragInfo = this.dragInfo_; + const state = bookmarks.Store.getInstance().data; if (isBookmarkList(overElement)) return DropPosition.NONE; @@ -856,16 +920,16 @@ cr.define('bookmarks', function() { if (getBookmarkNode(overElement).parentId == ROOT_NODE_ID) return DropPosition.NONE; - var isOverFolderNode = isBookmarkFolderNode(overElement); + const isOverFolderNode = isBookmarkFolderNode(overElement); // We can only drop between items in the tree if we have any folders. if (isOverFolderNode && !dragInfo.isDraggingFolders()) return DropPosition.NONE; - var validDropPositions = DropPosition.NONE; + let validDropPositions = DropPosition.NONE; // Cannot drop above if the item above is already in the drag source. - var previousElem = overElement.previousElementSibling; + const previousElem = overElement.previousElementSibling; if (!previousElem || !dragInfo.isDraggingBookmark(previousElem.itemId)) validDropPositions |= DropPosition.ABOVE; @@ -876,7 +940,7 @@ cr.define('bookmarks', function() { return validDropPositions; } - var nextElement = overElement.nextElementSibling; + const nextElement = overElement.nextElementSibling; // Cannot drop below if the item below is already in the drag source. if (!nextElement || !dragInfo.isDraggingBookmark(nextElement.itemId)) @@ -896,7 +960,7 @@ cr.define('bookmarks', function() { canDropOn_: function(overElement) { // Allow dragging onto empty bookmark lists. if (isBookmarkList(overElement)) { - var state = bookmarks.Store.getInstance().data; + const state = bookmarks.Store.getInstance().data; return state.selectedFolder && state.nodes[state.selectedFolder].children.length == 0; } @@ -917,7 +981,7 @@ cr.define('bookmarks', function() { isBookmarkList(dropDestination.element); }, - /** @param {bookmarks.TimerProxy} timerProxy */ + /** @param {!Object} timerProxy */ setTimerProxyForTesting: function(timerProxy) { this.timerProxy_ = timerProxy; this.dropIndicator_.timerProxy = timerProxy; @@ -937,6 +1001,8 @@ cr.define('bookmarks', function() { }; return { + AutoExpander: AutoExpander, + AutoScroller: AutoScroller, DNDManager: DNDManager, DragInfo: DragInfo, DropIndicator: DropIndicator, diff --git a/chromium/chrome/browser/resources/md_bookmarks/edit_dialog.js b/chromium/chrome/browser/resources/md_bookmarks/edit_dialog.js index d3138fa8b93..d46092be5fc 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/edit_dialog.js +++ b/chromium/chrome/browser/resources/md_bookmarks/edit_dialog.js @@ -82,7 +82,7 @@ Polymer({ * @private */ getDialogTitle_: function(isFolder, isEdit) { - var title; + let title; if (isEdit) title = isFolder ? 'renameFolderTitle' : 'editBookmarkTitle'; else @@ -98,8 +98,8 @@ Polymer({ * @private */ validateUrl_: function() { - var urlInput = /** @type {PaperInputElement} */ (this.$.url); - var originalValue = this.urlValue_; + const urlInput = /** @type {PaperInputElement} */ (this.$.url); + const originalValue = this.urlValue_; if (urlInput.validate()) return true; @@ -115,7 +115,7 @@ Polymer({ /** @private */ onSaveButtonTap_: function() { - var edit = {'title': this.titleValue_}; + const edit = {'title': this.titleValue_}; if (!this.isFolder_) { if (!this.validateUrl_()) return; diff --git a/chromium/chrome/browser/resources/md_bookmarks/folder_node.html b/chromium/chrome/browser/resources/md_bookmarks/folder_node.html index 57a4100e22a..182b0226544 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/folder_node.html +++ b/chromium/chrome/browser/resources/md_bookmarks/folder_node.html @@ -83,7 +83,7 @@ on-tap="selectFolder_" on-dblclick="toggleFolder_" on-contextmenu="onContextMenu_" - tabindex$="[[getTabIndex_(isSelectedFolder_)]]" + tabindex$="[[getTabIndex_(selectedFolder_, itemId)]]" hidden="[[isRootFolder_(depth)]]" role="treeitem"> <template is="dom-if" if="[[hasChildFolder_]]"> @@ -92,7 +92,7 @@ on-tap="toggleFolder_" on-mousedown="preventDefault_" tabindex="-1" - aria-hidden="true"> + aria-label$="[[getButtonAriaLabel_(isOpen, item_)]]"> <iron-icon icon="cr:arrow-drop-down" is-open$="[[isOpen]]"> </iron-icon> </button> diff --git a/chromium/chrome/browser/resources/md_bookmarks/folder_node.js b/chromium/chrome/browser/resources/md_bookmarks/folder_node.js index b84e0b4c8f0..ba8e361c0b9 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/folder_node.js +++ b/chromium/chrome/browser/resources/md_bookmarks/folder_node.js @@ -102,9 +102,9 @@ Polymer({ * @param {!Event} e */ onKeydown_: function(e) { - var yDirection = 0; - var xDirection = 0; - var handled = true; + let yDirection = 0; + let xDirection = 0; + let handled = true; if (e.key == 'ArrowUp') { yDirection = -1; } else if (e.key == 'ArrowDown') { @@ -113,6 +113,8 @@ Polymer({ xDirection = -1; } else if (e.key == 'ArrowRight') { xDirection = 1; + } else if (e.key == ' ') { + this.selectFolder_(); } else { handled = false; } @@ -142,8 +144,8 @@ Polymer({ * @param {!HTMLElement} currentFocus */ changeKeyboardSelection_: function(xDirection, yDirection, currentFocus) { - var newFocusFolderNode = null; - var isChildFolderNodeFocused = + let newFocusFolderNode = null; + const isChildFolderNodeFocused = currentFocus.tagName == 'BOOKMARKS-FOLDER-NODE'; if (xDirection == 1) { @@ -163,7 +165,7 @@ Polymer({ if (this.hasChildFolder_ && this.isOpen) { this.dispatch(bookmarks.actions.changeFolderOpen(this.item_.id, false)); } else { - var parentFolderNode = this.getParentFolderNode_(); + const parentFolderNode = this.getParentFolderNode_(); if (parentFolderNode.itemId != ROOT_NODE_ID) { parentFolderNode.selectFolder_(); parentFolderNode.getFocusTarget().focus(); @@ -176,7 +178,7 @@ Polymer({ // The current node's successor is its first child when open. if (!isChildFolderNodeFocused && yDirection == 1 && this.isOpen) { - var children = this.getChildFolderNodes_(); + const children = this.getChildFolderNodes_(); if (children.length) newFocusFolderNode = children[0]; } @@ -219,10 +221,10 @@ Polymer({ * before/after |child|. */ getNextChild_: function(reverse, child) { - var newFocus = null; - var children = this.getChildFolderNodes_(); + let newFocus = null; + const children = this.getChildFolderNodes_(); - var index = children.indexOf(child); + const index = children.indexOf(child); assert(index != -1); if (reverse) { // A child node's predecessor is either the previous child's last visible @@ -243,7 +245,7 @@ Polymer({ * @return {BookmarksFolderNodeElement|null} */ getParentFolderNode_: function() { - var parentFolderNode = this.parentNode; + let parentFolderNode = this.parentNode; while (parentFolderNode && parentFolderNode.tagName != 'BOOKMARKS-FOLDER-NODE') { parentFolderNode = parentFolderNode.parentNode || parentFolderNode.host; @@ -256,7 +258,7 @@ Polymer({ * @return {BookmarksFolderNodeElement} */ getLastVisibleDescendant_: function() { - var children = this.getChildFolderNodes_(); + const children = this.getChildFolderNodes_(); if (!this.isOpen || children.length == 0) return this; @@ -364,7 +366,10 @@ Polymer({ * @return {string} */ getTabIndex_: function() { - return this.isSelectedFolder_ ? '0' : '-1'; + // This returns a tab index of 0 for the cached selected folder when the + // search is active, even though this node is not technically selected. This + // allows the sidebar to be focusable during a search. + return this.selectedFolder_ == this.itemId ? '0' : '-1'; }, /** @@ -390,4 +395,14 @@ Polymer({ return openState != null ? openState : depth <= FOLDER_OPEN_BY_DEFAULT_DEPTH; }, + + /** + * @private + * @return {string} + */ + getButtonAriaLabel_: function() { + return loadTimeData.getStringF( + this.isOpen ? 'sidebarNodeCollapseAxLabel' : 'sidebarNodeExpandAxLabel', + this.item_.title); + }, }); diff --git a/chromium/chrome/browser/resources/md_bookmarks/item.html b/chromium/chrome/browser/resources/md_bookmarks/item.html index 30c219c6e3b..da6943d36fc 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/item.html +++ b/chromium/chrome/browser/resources/md_bookmarks/item.html @@ -51,6 +51,7 @@ class="more-vert-button" tabindex$="[[ironListTabIndex]]" title="$i18n{moreActionsButtonTitle}" + aria-label$="[[getButtonAriaLabel_(item_)]]" on-click="onMenuButtonClick_" on-dblclick="onMenuButtonDblClick_" aria-haspopup="menu"> diff --git a/chromium/chrome/browser/resources/md_bookmarks/item.js b/chromium/chrome/browser/resources/md_bookmarks/item.js index 91e3f038970..c3c12c3bede 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/item.js +++ b/chromium/chrome/browser/resources/md_bookmarks/item.js @@ -138,7 +138,7 @@ Polymer({ // Ignore double clicks so that Ctrl double-clicking an item won't deselect // the item before opening. if (e.detail != 2) { - var addKey = cr.isMac ? e.metaKey : e.ctrlKey; + const addKey = cr.isMac ? e.metaKey : e.ctrlKey; this.dispatch(bookmarks.actions.selectItem(this.itemId, this.getState(), { clear: !addKey, range: e.shiftKey, @@ -168,8 +168,8 @@ Polymer({ if (!this.isSelectedItem_) this.selectThisItem_(); - var commandManager = bookmarks.CommandManager.getInstance(); - var itemSet = this.getState().selection.items; + const commandManager = bookmarks.CommandManager.getInstance(); + const itemSet = this.getState().selection.items; if (commandManager.canExecute(Command.OPEN, itemSet)) commandManager.handle(Command.OPEN, itemSet); }, @@ -186,9 +186,9 @@ Polymer({ if (this.isFolder_) return; - var commandManager = bookmarks.CommandManager.getInstance(); - var itemSet = this.getState().selection.items; - var command = e.shiftKey ? Command.OPEN : Command.OPEN_NEW_TAB; + const commandManager = bookmarks.CommandManager.getInstance(); + const itemSet = this.getState().selection.items; + const command = e.shiftKey ? Command.OPEN : Command.OPEN_NEW_TAB; if (commandManager.canExecute(command, itemSet)) commandManager.handle(command, itemSet); }, @@ -212,4 +212,10 @@ Polymer({ this.$.icon.className = url ? 'website-icon' : 'folder-icon'; this.$.icon.style.backgroundImage = url ? cr.icon.getFavicon(url) : null; }, + + /** @private */ + getButtonAriaLabel_: function() { + return loadTimeData.getStringF( + 'moreActionsButtonAxLabel', this.item_.title); + } }); diff --git a/chromium/chrome/browser/resources/md_bookmarks/list.html b/chromium/chrome/browser/resources/md_bookmarks/list.html index 00f86dbff22..2f5ed7aa577 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/list.html +++ b/chromium/chrome/browser/resources/md_bookmarks/list.html @@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.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-icon-button/paper-icon-button-light.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/shadow.html"> @@ -38,8 +39,11 @@ user-select: none; } </style> - <iron-list id="list" items="[[displayedList_]]" - hidden$="[[isEmptyList_(displayedList_.length)]]" role="list"> + <iron-list id="list" + items="[[displayedList_]]" + hidden$="[[isEmptyList_(displayedList_.length)]]" + role="group" + aria-label="$i18n{listAxLabel}"> <template> <bookmarks-item item-id="[[item.id]]" draggable="true" tabindex$="[[tabIndex]]" iron-list-tab-index="[[tabIndex]]"> diff --git a/chromium/chrome/browser/resources/md_bookmarks/list.js b/chromium/chrome/browser/resources/md_bookmarks/list.js index 823f955e8b1..3898fc089c9 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/list.js +++ b/chromium/chrome/browser/resources/md_bookmarks/list.js @@ -50,7 +50,7 @@ Polymer({ }, attached: function() { - var list = /** @type {IronListElement} */ (this.$.list); + const list = /** @type {IronListElement} */ (this.$.list); list.scrollTarget = this; this.watch('displayedIds_', function(state) { @@ -70,6 +70,10 @@ Polymer({ /** @private {function(!Event)} */ this.boundOnHighlightItems_ = this.onHighlightItems_.bind(this); document.addEventListener('highlight-items', this.boundOnHighlightItems_); + + Polymer.RenderStatus.afterNextRender(this, function() { + Polymer.IronA11yAnnouncer.requestAvailability(); + }); }, detached: function() { @@ -95,12 +99,12 @@ Polymer({ return {id: id}; }); } else { - var splices = Polymer.ArraySplice.calculateSplices( + const splices = Polymer.ArraySplice.calculateSplices( /** @type {!Array<string>} */ (newValue), /** @type {!Array<string>} */ (oldValue)); splices.forEach((splice) => { // TODO(calamity): Could use notifySplices to improve performance here. - var additions = + const additions = newValue.slice(splice.index, splice.index + splice.addedCount) .map(function(id) { return {id: id}; @@ -109,6 +113,10 @@ Polymer({ 'displayedList_', splice.index, splice.removed.length ].concat(additions)); }); + + cr.sendWithPromise( + 'getPluralString', 'listChanged', this.displayedList_.length) + .then((label) => this.fire('iron-announce', {text: label})); } }, @@ -123,8 +131,8 @@ Polymer({ * @private */ scrollToId_: function(itemId) { - var index = this.displayedIds_.indexOf(itemId); - var list = this.$.list; + const index = this.displayedIds_.indexOf(itemId); + const list = this.$.list; if (index >= 0 && index < list.firstVisibleIndex || index > list.lastVisibleIndex) { list.scrollToIndex(index); @@ -133,7 +141,7 @@ Polymer({ /** @private */ emptyListMessage_: function() { - var emptyListMessage = this.searchTerm_ ? 'noSearchResults' : 'emptyList'; + const emptyListMessage = this.searchTerm_ ? 'noSearchResults' : 'emptyList'; return loadTimeData.getString(emptyListMessage); }, @@ -174,18 +182,18 @@ Polymer({ // Ensure that we only select items which are actually being displayed. // This should only matter if an unrelated update to the bookmark model // happens with the perfect timing to end up in a tracked batch update. - var toHighlight = /** @type {!Array<string>} */ + const toHighlight = /** @type {!Array<string>} */ (e.detail.filter((item) => this.displayedIds_.indexOf(item) != -1)); assert(toHighlight.length > 0); - var leadId = toHighlight[0]; + const leadId = toHighlight[0]; this.dispatch( bookmarks.actions.selectAll(toHighlight, this.getState(), leadId)); // Allow iron-list time to render additions to the list. this.async(function() { this.scrollToId_(leadId); - var leadIndex = this.displayedIds_.indexOf(leadId); + const leadIndex = this.displayedIds_.indexOf(leadId); assert(leadIndex != -1); this.$.list.focusItem(leadIndex); }); @@ -196,13 +204,13 @@ Polymer({ * @private */ onItemKeydown_: function(e) { - var handled = true; - var list = this.$.list; - var focusMoved = false; - var focusedIndex = + let handled = true; + const list = this.$.list; + let focusMoved = false; + let focusedIndex = this.getIndexForItemElement_(/** @type {HTMLElement} */ (e.target)); - var oldFocusedIndex = focusedIndex; - var cursorModifier = cr.isMac ? e.metaKey : e.ctrlKey; + const oldFocusedIndex = focusedIndex; + const cursorModifier = cr.isMac ? e.metaKey : e.ctrlKey; if (e.key == 'ArrowUp') { focusedIndex--; focusMoved = true; @@ -243,7 +251,7 @@ Polymer({ // If the focus moved from something other than a Ctrl + move event, // update the selection. - var config = { + const config = { clear: !cursorModifier, range: e.shiftKey, toggle: false, diff --git a/chromium/chrome/browser/resources/md_bookmarks/mouse_focus_behavior.js b/chromium/chrome/browser/resources/md_bookmarks/mouse_focus_behavior.js index 63755568cee..32d505c0f91 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/mouse_focus_behavior.js +++ b/chromium/chrome/browser/resources/md_bookmarks/mouse_focus_behavior.js @@ -4,7 +4,7 @@ cr.define('bookmarks', function() { /** @const */ - var HIDE_FOCUS_RING_ATTRIBUTE = 'hide-focus-ring'; + const HIDE_FOCUS_RING_ATTRIBUTE = 'hide-focus-ring'; /** * Behavior which adds the 'hide-focus-ring' attribute to a target element @@ -12,7 +12,7 @@ cr.define('bookmarks', function() { * to be hidden without affecting keyboard users. * @polymerBehavior */ - var MouseFocusBehavior = { + const MouseFocusBehavior = { attached: function() { this.boundOnMousedown_ = this.onMousedown_.bind(this); this.boundOnKeydown = this.onKeydown_.bind(this); diff --git a/chromium/chrome/browser/resources/md_bookmarks/reducers.js b/chromium/chrome/browser/resources/md_bookmarks/reducers.js index 2255f7971ef..a307e5551e2 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/reducers.js +++ b/chromium/chrome/browser/resources/md_bookmarks/reducers.js @@ -10,7 +10,7 @@ */ cr.define('bookmarks', function() { - var SelectionState = {}; + const SelectionState = {}; /** * @param {SelectionState} selectionState @@ -18,12 +18,12 @@ cr.define('bookmarks', function() { * @return {SelectionState} */ SelectionState.selectItems = function(selectionState, action) { - var newItems = new Set(); + let newItems = new Set(); if (!action.clear) newItems = new Set(selectionState.items); action.items.forEach(function(id) { - var add = true; + let add = true; if (action.toggle) add = !newItems.has(id); @@ -107,7 +107,7 @@ cr.define('bookmarks', function() { } }; - var SearchState = {}; + const SearchState = {}; /** * @param {SearchState} search @@ -152,7 +152,7 @@ cr.define('bookmarks', function() { if (!search.results) return search; - var newResults = []; + const newResults = []; search.results.forEach(function(id) { if (!deletedIds.has(id)) newResults.push(id); @@ -183,7 +183,7 @@ cr.define('bookmarks', function() { } }; - var NodeState = {}; + const NodeState = {}; /** * @param {NodeMap} nodes @@ -192,7 +192,7 @@ cr.define('bookmarks', function() { * @return {NodeMap} */ NodeState.modifyNode_ = function(nodes, id, callback) { - var nodeModification = {}; + const nodeModification = {}; nodeModification[id] = callback(nodes[id]); return Object.assign({}, nodes, nodeModification); }; @@ -203,11 +203,11 @@ cr.define('bookmarks', function() { * @return {NodeMap} */ NodeState.createBookmark = function(nodes, action) { - var nodeModifications = {}; + const nodeModifications = {}; nodeModifications[action.id] = action.node; - var parentNode = nodes[action.parentId]; - var newChildren = parentNode.children.slice(); + const parentNode = nodes[action.parentId]; + const newChildren = parentNode.children.slice(); newChildren.splice(action.parentIndex, 0, action.id); nodeModifications[action.parentId] = Object.assign({}, parentNode, { children: newChildren, @@ -238,23 +238,23 @@ cr.define('bookmarks', function() { * @return {NodeMap} */ NodeState.moveBookmark = function(nodes, action) { - var nodeModifications = {}; - var id = action.id; + const nodeModifications = {}; + const id = action.id; // Change node's parent. nodeModifications[id] = Object.assign({}, nodes[id], {parentId: action.parentId}); // Remove from old parent. - var oldParentId = action.oldParentId; - var oldParentChildren = nodes[oldParentId].children.slice(); + const oldParentId = action.oldParentId; + const oldParentChildren = nodes[oldParentId].children.slice(); oldParentChildren.splice(action.oldIndex, 1); nodeModifications[oldParentId] = Object.assign({}, nodes[oldParentId], {children: oldParentChildren}); // Add to new parent. - var parentId = action.parentId; - var parentChildren = oldParentId == parentId ? + const parentId = action.parentId; + const parentChildren = oldParentId == parentId ? oldParentChildren : nodes[parentId].children.slice(); parentChildren.splice(action.index, 0, action.id); @@ -270,9 +270,9 @@ cr.define('bookmarks', function() { * @return {NodeMap} */ NodeState.removeBookmark = function(nodes, action) { - var newState = + const newState = NodeState.modifyNode_(nodes, action.parentId, function(node) { - var newChildren = node.children.slice(); + const newChildren = node.children.slice(); newChildren.splice(action.index, 1); return /** @type {BookmarkNode} */ ( Object.assign({}, node, {children: newChildren})); @@ -317,7 +317,7 @@ cr.define('bookmarks', function() { } }; - var SelectedFolderState = {}; + const SelectedFolderState = {}; /** * @param {NodeMap} nodes @@ -326,7 +326,7 @@ cr.define('bookmarks', function() { * @return {boolean} */ SelectedFolderState.isAncestorOf = function(nodes, ancestorId, childId) { - var currentId = childId; + let currentId = childId; // Work upwards through the tree from child. while (currentId) { if (currentId == ancestorId) @@ -368,7 +368,7 @@ cr.define('bookmarks', function() { } }; - var FolderOpenState = {}; + const FolderOpenState = {}; /** * @param {FolderOpenState} folderOpenState @@ -378,9 +378,9 @@ cr.define('bookmarks', function() { */ FolderOpenState.openFolderAndAncestors = function( folderOpenState, id, nodes) { - var newFolderOpenState = + const newFolderOpenState = /** @type {FolderOpenState} */ (new Map(folderOpenState)); - for (var currentId = id; currentId; currentId = nodes[currentId].parentId) + for (let currentId = id; currentId; currentId = nodes[currentId].parentId) newFolderOpenState.set(currentId, true); return newFolderOpenState; @@ -392,7 +392,7 @@ cr.define('bookmarks', function() { * @return {FolderOpenState} */ FolderOpenState.changeFolderOpen = function(folderOpenState, action) { - var newFolderOpenState = + const newFolderOpenState = /** @type {FolderOpenState} */ (new Map(folderOpenState)); newFolderOpenState.set(action.id, action.open); @@ -427,7 +427,7 @@ cr.define('bookmarks', function() { } }; - var PreferencesState = {}; + const PreferencesState = {}; /** * @param {PreferencesState} prefs diff --git a/chromium/chrome/browser/resources/md_bookmarks/router.js b/chromium/chrome/browser/resources/md_bookmarks/router.js index 89267f9ef2b..144586d47d4 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/router.js +++ b/chromium/chrome/browser/resources/md_bookmarks/router.js @@ -49,8 +49,8 @@ Polymer({ /** @private */ onQueryParamsChanged_: function() { - var searchTerm = this.queryParams_.q || ''; - var selectedId = this.queryParams_.id; + const searchTerm = this.queryParams_.q || ''; + let selectedId = this.queryParams_.id; if (!selectedId && !searchTerm) selectedId = BOOKMARKS_BAR_ID; diff --git a/chromium/chrome/browser/resources/md_bookmarks/store.js b/chromium/chrome/browser/resources/md_bookmarks/store.js index 0b679e2a5c1..3b0f7cad98a 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/store.js +++ b/chromium/chrome/browser/resources/md_bookmarks/store.js @@ -55,7 +55,7 @@ cr.define('bookmarks', function() { /** @param {!StoreObserver} observer */ removeObserver: function(observer) { - var index = this.observers_.indexOf(observer); + const index = this.observers_.indexOf(observer); this.observers_.splice(index, 1); }, diff --git a/chromium/chrome/browser/resources/md_bookmarks/store_client.js b/chromium/chrome/browser/resources/md_bookmarks/store_client.js index b7149ea2831..bc5b4342930 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/store_client.js +++ b/chromium/chrome/browser/resources/md_bookmarks/store_client.js @@ -12,7 +12,7 @@ cr.define('bookmarks', function() { * @polymerBehavior * @implements {StoreObserver} */ - var StoreClient = { + const StoreClient = { created: function() { /** * @type {!Array<{ @@ -80,8 +80,8 @@ cr.define('bookmarks', function() { /** @param {string} newState */ onStateChanged: function(newState) { this.watches_.forEach((watch) => { - var oldValue = this[watch.localProperty]; - var newValue = watch.valueGetter(newState); + const oldValue = this[watch.localProperty]; + const newValue = watch.valueGetter(newState); // Avoid poking Polymer unless something has actually changed. Reducers // must return new objects rather than mutating existing objects, so diff --git a/chromium/chrome/browser/resources/md_bookmarks/timer_proxy.js b/chromium/chrome/browser/resources/md_bookmarks/timer_proxy.js deleted file mode 100644 index 091023fecb5..00000000000 --- a/chromium/chrome/browser/resources/md_bookmarks/timer_proxy.js +++ /dev/null @@ -1,89 +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. - -/** - * @fileoverview A class that mediates window timer calls to support mockability - * in tests. - */ - -cr.define('bookmarks', function() { - /** @constructor */ - function TimerProxy() {} - - TimerProxy.prototype = { - /** - * @param {function()|string} fn - * @param {number=} delay - * @return {number} - */ - setTimeout: function(fn, delay) { - return window.setTimeout(fn, delay); - }, - - /** @param {number|undefined?} id */ - clearTimeout: function(id) { - window.clearTimeout(id); - }, - }; - - /** - * A one-shot debouncer which fires the given callback after a delay. The - * delay can be refreshed by calling resetTimer. Resetting with no delay moves - * the callback to the end of the task queue. - * @param {!function()} callback - * @constructor - */ - function Debouncer(callback) { - /** @private {!function()} */ - this.callback_ = callback; - /** @private {!bookmarks.TimerProxy} */ - this.timerProxy_ = new TimerProxy(); - /** @private {?number} */ - this.timer_ = null; - /** @private {!function()} */ - this.boundTimerCallback_ = this.timerCallback_.bind(this); - /** @private {boolean} */ - this.isDone_ = false; - /** @private {!PromiseResolver} */ - this.promiseResolver_ = new PromiseResolver(); - } - - Debouncer.prototype = { - /** - * @param {number=} delay - */ - resetTimeout: function(delay) { - if (this.timer_) - this.timerProxy_.clearTimeout(this.timer_); - this.timer_ = - this.timerProxy_.setTimeout(this.boundTimerCallback_, delay); - }, - - /** - * @return {boolean} True if the Debouncer has finished processing. - */ - done: function() { - return this.isDone_; - }, - - /** - * @return {Promise} Promise which resolves immediately after the callback. - */ - get promise() { - return this.promiseResolver_.promise; - }, - - /** @private */ - timerCallback_: function() { - this.isDone_ = true; - this.callback_.call(); - this.promiseResolver_.resolve(); - }, - }; - - return { - Debouncer: Debouncer, - TimerProxy: TimerProxy, - }; -}); diff --git a/chromium/chrome/browser/resources/md_bookmarks/toast_manager.html b/chromium/chrome/browser/resources/md_bookmarks/toast_manager.html index cb30e2209fd..0e5b0b60e1f 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/toast_manager.html +++ b/chromium/chrome/browser/resources/md_bookmarks/toast_manager.html @@ -1,12 +1,14 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_toast/cr_toast.html"> <link rel="import" href="chrome://bookmarks/shared_style.html"> <dom-module id="bookmarks-toast-manager"> <template> <style include="shared-style"> #content { + color: #fff; display: flex; flex: 1; overflow: hidden; @@ -32,39 +34,13 @@ span { white-space: pre; } - - #toast { - align-items: center; - background-color: #323232; - border-radius: 4px; - bottom: 0; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.28); - box-sizing: border-box; - color: #fff; - display: flex; - margin: 24px; - max-width: 568px; - min-height: 52px; - min-width: 288px; - opacity: 0; - padding: 0 24px; - position: fixed; - transform: translateY(100px); - transition: transform 300ms, opacity 300ms; - white-space: nowrap; - } - - #toast[aria-hidden=false] { - opacity: 1; - transform: translateY(0); - } </style> - <div id="toast"> + <cr-toast id="toast" duration="[[duration]]"> <div id="content"></div> <paper-button id="button" hidden$="[[!showUndo_]]" on-tap="onUndoTap_"> $i18n{undo} </paper-button> - </div> + </cr-toast> </template> <script src="chrome://bookmarks/toast_manager.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_bookmarks/toast_manager.js b/chromium/chrome/browser/resources/md_bookmarks/toast_manager.js index d2da7a5d5f8..a489b15db48 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/toast_manager.js +++ b/chromium/chrome/browser/resources/md_bookmarks/toast_manager.js @@ -7,7 +7,7 @@ */ cr.define('bookmarks', function() { - var ToastManager = Polymer({ + const ToastManager = Polymer({ is: 'bookmarks-toast-manager', properties: { @@ -17,22 +17,9 @@ cr.define('bookmarks', function() { }, /** @private */ - open_: { - type: Boolean, - observer: 'openChanged_', - value: false, - }, - - /** @private */ showUndo_: Boolean, }, - /** @private {bookmarks.TimerProxy} */ - timerProxy_: new bookmarks.TimerProxy(), - - /** @private {number|null} */ - hideTimeoutId_: null, - /** @override */ attached: function() { assert(ToastManager.instance_ == null); @@ -54,6 +41,7 @@ cr.define('bookmarks', function() { show: function(label, showUndo) { this.$.content.textContent = label; this.showInternal_(showUndo); + this.$.toast.show(); }, /** @@ -62,13 +50,13 @@ cr.define('bookmarks', function() { * @param {boolean} showUndo Whether the undo button should be shown. */ showForStringPieces: function(pieces, showUndo) { - var content = this.$.content; + const content = this.$.content; content.textContent = ''; pieces.forEach(function(p) { if (p.value.length == 0) return; - var span = document.createElement('span'); + const span = document.createElement('span'); span.textContent = p.value; if (p.collapsible) span.classList.add('collapsible'); @@ -84,26 +72,13 @@ cr.define('bookmarks', function() { * @private */ showInternal_: function(showUndo) { - this.open_ = true; this.showUndo_ = showUndo; this.fire('iron-announce', {text: this.$.content.textContent}); - - if (!this.duration) - return; - - if (this.hideTimeoutId_ != null) - this.timerProxy_.clearTimeout(this.hideTimeoutId_); - - this.hideTimeoutId_ = this.timerProxy_.setTimeout(() => { - this.hide(); - this.hideTimeoutId_ = null; - }, this.duration); + this.$.toast.show(); }, hide: function() { - this.open_ = false; - // Hide the undo button to prevent it from being accessed with tab. - this.showUndo_ = false; + this.$.toast.hide(); }, /** @private */ @@ -111,11 +86,6 @@ cr.define('bookmarks', function() { // Will hide the toast. this.fire('command-undo'); }, - - /** @private */ - openChanged_: function() { - this.$.toast.setAttribute('aria-hidden', String(!this.open_)); - }, }); /** @private {?bookmarks.ToastManager} */ diff --git a/chromium/chrome/browser/resources/md_bookmarks/toolbar.js b/chromium/chrome/browser/resources/md_bookmarks/toolbar.js index 5923a896234..26ef60991b6 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/toolbar.js +++ b/chromium/chrome/browser/resources/md_bookmarks/toolbar.js @@ -94,7 +94,7 @@ Polymer({ * @private */ onMenuButtonOpenTap_: function(e) { - var menu = /** @type {!CrActionMenuElement} */ (this.$.dropdown.get()); + const menu = /** @type {!CrActionMenuElement} */ (this.$.dropdown.get()); menu.showAt(/** @type {!Element} */ (e.target)); }, @@ -108,7 +108,7 @@ Polymer({ /** @private */ onAddBookmarkTap_: function() { - var dialog = + const dialog = /** @type {BookmarksEditDialogElement} */ (this.$.addDialog.get()); dialog.showAddDialog(false, assert(this.selectedFolder_)); this.closeDropdownMenu_(); @@ -116,7 +116,7 @@ Polymer({ /** @private */ onAddFolderTap_: function() { - var dialog = + const dialog = /** @type {BookmarksEditDialogElement} */ (this.$.addDialog.get()); dialog.showAddDialog(true, assert(this.selectedFolder_)); this.closeDropdownMenu_(); @@ -136,8 +136,8 @@ Polymer({ /** @private */ onDeleteSelectionTap_: function() { - var selection = this.selectedItems_; - var commandManager = bookmarks.CommandManager.getInstance(); + const selection = this.selectedItems_; + const commandManager = bookmarks.CommandManager.getInstance(); assert(commandManager.canExecute(Command.DELETE, selection)); commandManager.handle(Command.DELETE, selection); }, @@ -149,7 +149,7 @@ Polymer({ /** @private */ closeDropdownMenu_: function() { - var menu = /** @type {!CrActionMenuElement} */ (this.$.dropdown.get()); + const menu = /** @type {!CrActionMenuElement} */ (this.$.dropdown.get()); menu.close(); }, @@ -158,7 +158,7 @@ Polymer({ * @private */ onSearchChanged_: function(e) { - var searchTerm = /** @type {string} */ (e.detail); + const searchTerm = /** @type {string} */ (e.detail); if (searchTerm != this.searchTerm_) this.dispatch(bookmarks.actions.setSearchTerm(searchTerm)); }, diff --git a/chromium/chrome/browser/resources/md_bookmarks/types.js b/chromium/chrome/browser/resources/md_bookmarks/types.js index 560cf65d61c..3ff4e59fe53 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/types.js +++ b/chromium/chrome/browser/resources/md_bookmarks/types.js @@ -19,12 +19,12 @@ * children: (!Array<string>|undefined), * }} */ -var BookmarkNode; +let BookmarkNode; /** * @typedef {!Object<string, BookmarkNode>} */ -var NodeMap; +let NodeMap; /** * @typedef {{ @@ -34,7 +34,7 @@ var NodeMap; * * |items| is used as a set and all values in the map are true. */ -var SelectionState; +let SelectionState; /** * Note: @@ -53,10 +53,10 @@ var SelectionState; * results: ?Array<string>, * }} */ -var SearchState; +let SearchState; /** @typedef {!Map<string, boolean>} */ -var FolderOpenState; +let FolderOpenState; /** * @typedef {{ @@ -64,7 +64,7 @@ var FolderOpenState; * incognitoAvailability: IncognitoAvailability, * }} */ -var PreferencesState; +let PreferencesState; /** * @typedef {{ @@ -76,16 +76,16 @@ var PreferencesState; * selection: SelectionState, * }} */ -var BookmarksPageState; +let BookmarksPageState; /** @typedef {{name: string}} */ -var Action; +let Action; /** @typedef {function(function(?Action))} */ -var DeferredAction; +let DeferredAction; /** @typedef {{element: BookmarkElement, position: DropPosition}} */ -var DropDestination; +let DropDestination; /** * @record diff --git a/chromium/chrome/browser/resources/md_bookmarks/util.js b/chromium/chrome/browser/resources/md_bookmarks/util.js index acdd4240e9d..ea85762bb86 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/util.js +++ b/chromium/chrome/browser/resources/md_bookmarks/util.js @@ -25,7 +25,7 @@ cr.define('bookmarks.util', function() { * @return {!BookmarkNode} */ function normalizeNode(treeNode) { - var node = Object.assign({}, treeNode); + const node = Object.assign({}, treeNode); // Node index is not necessary and not kept up-to-date. Remove it from the // data structure so we don't accidentally depend on the incorrect // information. @@ -46,12 +46,12 @@ cr.define('bookmarks.util', function() { */ function normalizeNodes(rootNode) { /** @type {NodeMap} */ - var nodeMap = {}; - var stack = []; + const nodeMap = {}; + const stack = []; stack.push(rootNode); while (stack.length > 0) { - var node = stack.pop(); + const node = stack.pop(); nodeMap[node.id] = normalizeNode(node); if (!node.children) continue; @@ -127,8 +127,8 @@ cr.define('bookmarks.util', function() { * @return {boolean} */ function hasChildFolders(id, nodes) { - var children = nodes[id].children; - for (var i = 0; i < children.length; i++) { + const children = nodes[id].children; + for (let i = 0; i < children.length; i++) { if (nodes[children[i]].children) return true; } @@ -142,13 +142,13 @@ cr.define('bookmarks.util', function() { * @return {!Set<string>} */ function getDescendants(nodes, baseId) { - var descendants = new Set(); - var stack = []; + const descendants = new Set(); + const stack = []; stack.push(baseId); while (stack.length > 0) { - var id = stack.pop(); - var node = nodes[id]; + const id = stack.pop(); + const node = nodes[id]; if (!node) continue; @@ -182,7 +182,7 @@ cr.define('bookmarks.util', function() { * @template T */ function removeIdsFromObject(map, ids) { - var newObject = Object.assign({}, map); + const newObject = Object.assign({}, map); ids.forEach(function(id) { delete newObject[id]; }); @@ -197,7 +197,7 @@ cr.define('bookmarks.util', function() { * @template T */ function removeIdsFromMap(map, ids) { - var newMap = new Map(map); + const newMap = new Map(map); ids.forEach(function(id) { newMap.delete(id); }); @@ -210,7 +210,7 @@ cr.define('bookmarks.util', function() { * @return {!Set<string>} */ function removeIdsFromSet(set, ids) { - var difference = new Set(set); + const difference = new Set(set); ids.forEach(function(id) { difference.delete(id); }); diff --git a/chromium/chrome/browser/resources/md_downloads/BUILD.gn b/chromium/chrome/browser/resources/md_downloads/BUILD.gn index 1b340e3511c..f741fce8c0b 100644 --- a/chromium/chrome/browser/resources/md_downloads/BUILD.gn +++ b/chromium/chrome/browser/resources/md_downloads/BUILD.gn @@ -1,6 +1,6 @@ -import("../vulcanize.gni") +import("../optimize_webui.gni") -vulcanize("build") { +optimize_webui("build") { deps = [] host = "downloads" html_in_files = [ "downloads.html" ] diff --git a/chromium/chrome/browser/resources/md_downloads/action_service_unittest.gtestjs b/chromium/chrome/browser/resources/md_downloads/action_service_unittest.gtestjs deleted file mode 100644 index bc3524bc5f9..00000000000 --- a/chromium/chrome/browser/resources/md_downloads/action_service_unittest.gtestjs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015 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. - -/** - * @param {!Array<string>} list - * @return {string} - */ -function str(list) { - return JSON.stringify(list); -} - -var ActionServiceUnitTest = class extends testing.Test { - /** @override */ - get extraLibraries() { - return [ - '../../../../ui/webui/resources/js/cr.js', - 'browser_proxy.js', - 'action_service.js', - ]; - } -} - -TEST_F('ActionServiceUnitTest', 'splitTerms', function() { - const ActionService = downloads.ActionService; - assertEquals(str([]), str(ActionService.splitTerms(''))); - assertEquals(str([]), str(ActionService.splitTerms(' '))); - assertEquals(str(['a']), str(ActionService.splitTerms('a'))); - assertEquals(str(['a b']), str(ActionService.splitTerms('a b'))); - assertEquals(str(['a', 'b']), str(ActionService.splitTerms('a "b"'))); - assertEquals(str(['a', 'b', 'c']), str(ActionService.splitTerms('a "b" c'))); - assertEquals(str(['a', 'b b', 'c']), - str(ActionService.splitTerms('a "b b" c'))); -}); - -TEST_F('ActionServiceUnitTest', 'searchWithSimilarTerms', function() { - class TestActionService extends downloads.ActionService { - loadMore() { /* No chrome.send() for you! */ } - } - - const actionService = new TestActionService(); - - assertTrue(actionService.search('a')); - assertFalse(actionService.search('a ')); // Same term + space should no-op. -}); diff --git a/chromium/chrome/browser/resources/md_downloads/compiled_resources2.gyp b/chromium/chrome/browser/resources/md_downloads/compiled_resources2.gyp index 19accde00e5..b9e2ca905c3 100644 --- a/chromium/chrome/browser/resources/md_downloads/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/md_downloads/compiled_resources2.gyp @@ -4,15 +4,6 @@ { 'targets': [ { - 'target_name': 'action_service', - 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', - 'browser_proxy', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { 'target_name': 'browser_proxy', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', @@ -58,9 +49,9 @@ '<(DEPTH)/ui/webui/resources/js/cr/compiled_resources2.gyp:ui', '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:command', '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-list/compiled_resources2.gyp:iron-list-extracted', - 'action_service', 'browser_proxy', 'item', + 'search_service', 'toolbar', '<(EXTERNS_GYP):chrome_send', 'externs', @@ -68,6 +59,15 @@ 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { + 'target_name': 'search_service', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + 'browser_proxy', + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'toolbar', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', @@ -76,8 +76,8 @@ '<(DEPTH)/ui/webui/resources/cr_elements/cr_toolbar/compiled_resources2.gyp:cr_toolbar', '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-a11y-announcer/compiled_resources2.gyp:iron-a11y-announcer-extracted', '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-menu/compiled_resources2.gyp:paper-menu-extracted', - 'action_service', 'browser_proxy', + 'search_service', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, diff --git a/chromium/chrome/browser/resources/md_downloads/manager.html b/chromium/chrome/browser/resources/md_downloads/manager.html index ef562191652..c8c68366be5 100644 --- a/chromium/chrome/browser/resources/md_downloads/manager.html +++ b/chromium/chrome/browser/resources/md_downloads/manager.html @@ -1,17 +1,18 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <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/command.html"> <link rel="import" href="chrome://resources/html/util.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html"> -<link rel="import" href="chrome://downloads/action_service.html"> <link rel="import" href="chrome://downloads/browser_proxy.html"> <link rel="import" href="chrome://downloads/constants.html"> <link rel="import" href="chrome://downloads/i18n_setup.html"> <link rel="import" href="chrome://downloads/item.html"> +<link rel="import" href="chrome://downloads/search_service.html"> <link rel="import" href="chrome://downloads/toolbar.html"> <dom-module id="downloads-manager"> @@ -37,20 +38,12 @@ z-index: 1; } - #toolbar::after { - box-shadow: inset 0 5px 6px -3px rgba(0, 0, 0, 0.4); - content: ''; - display: block; - height: 6px; - opacity: 0; - position: absolute; - top: 100%; - transition: opacity 500ms; - width: 100%; + #drop-shadow { + @apply(--cr-container-shadow); } - :host([has-shadow_]) #toolbar::after { - opacity: 1; + :host([has-shadow_]) #drop-shadow { + opacity: var(--cr-container-shadow-max-opacity); } #downloads-list { @@ -95,6 +88,7 @@ <downloads-toolbar id="toolbar" spinner-active="{{spinnerActive_}}" role="none"> </downloads-toolbar> + <div id="drop-shadow"></div> <iron-list id="downloads-list" items="[[items_]]" hidden="[[!hasDownloads_]]"> <template> diff --git a/chromium/chrome/browser/resources/md_downloads/manager.js b/chromium/chrome/browser/resources/md_downloads/manager.js index f6471eafbb0..3716f45b567 100644 --- a/chromium/chrome/browser/resources/md_downloads/manager.js +++ b/chromium/chrome/browser/resources/md_downloads/manager.js @@ -58,8 +58,8 @@ cr.define('downloads', function() { /** @private {?downloads.BrowserProxy} */ browserProxy_: null, - /** @private {?downloads.ActionService} */ - actionService_: null, + /** @private {?downloads.SearchService} */ + searchService_: null, /** @private {!PromiseResolver} */ loaded_: new PromiseResolver, @@ -67,7 +67,7 @@ cr.define('downloads', function() { /** @override */ created: function() { this.browserProxy_ = downloads.BrowserProxy.getInstance(); - this.actionService_ = downloads.ActionService.getInstance(); + this.searchService_ = downloads.SearchService.getInstance(); }, /** @override */ @@ -95,8 +95,16 @@ cr.define('downloads', function() { * @private */ insertItems_: function(index, list) { - this.splice.apply(this, ['items_', index, 0].concat(list)); + // Insert |list| at the given |index| via Array#splice(). + this.items_.splice.apply(this.items_, [index, 0].concat(list)); this.updateHideDates_(index, index + list.length); + this.notifySplices('items_', [{ + index: index, + addedCount: list.length, + object: this.items_, + type: 'splice', + removed: [], + }]); if (this.hasAttribute('loading')) { this.removeAttribute('loading'); @@ -167,7 +175,7 @@ cr.define('downloads', function() { const list = this.$['downloads-list']; if (list.scrollHeight - list.scrollTop - list.offsetHeight <= 100) { // Approaching the end of the scrollback. Attempt to load more items. - this.actionService_.loadMore(); + this.searchService_.loadMore(); } this.hasShadow_ = list.scrollTop > 0; }, @@ -181,13 +189,13 @@ cr.define('downloads', function() { document.addEventListener('canExecute', this.onCanExecute_.bind(this)); document.addEventListener('command', this.onCommand_.bind(this)); - this.actionService_.loadMore(); + this.searchService_.loadMore(); return this.loaded_.promise; }, /** @private */ onSearchChanged_: function() { - this.inSearchMode_ = this.actionService_.isSearching(); + this.inSearchMode_ = this.searchService_.isSearching(); }, /** @@ -195,12 +203,22 @@ cr.define('downloads', function() { * @private */ removeItem_: function(index) { - this.splice('items_', index, 1); + let removed = this.items_.splice(index, 1); this.updateHideDates_(index, index); + this.notifySplices('items_', [{ + index: index, + addedCount: 0, + object: this.items_, + type: 'splice', + removed: removed, + }]); this.onListScroll_(); }, /** + * Updates whether dates should show for |this.items_[start - end]|. Note: + * this method does not trigger template bindings. Use notifySplices() or + * after calling this method to ensure items are redrawn. * @param {number} start * @param {number} end * @private @@ -211,8 +229,7 @@ cr.define('downloads', function() { if (!current) continue; const prev = this.items_[i - 1]; - const hideDate = !!prev && prev.date_string == current.date_string; - this.set('items_.' + i + '.hideDate', hideDate); + current.hideDate = !!prev && prev.date_string == current.date_string; } }, @@ -222,8 +239,15 @@ cr.define('downloads', function() { * @private */ updateItem_: function(index, data) { - this.set('items_.' + index, data); + this.items_[index] = data; this.updateHideDates_(index, index); + this.notifySplices('items_', [{ + index: index, + addedCount: 0, + object: this.items_, + type: 'splice', + removed: [], + }]); const list = /** @type {!IronListElement} */ (this.$['downloads-list']); list.updateSizeForItem(index); }, diff --git a/chromium/chrome/browser/resources/md_downloads/action_service.html b/chromium/chrome/browser/resources/md_downloads/search_service.html index 7d59ba9e2f0..f37b195b301 100644 --- a/chromium/chrome/browser/resources/md_downloads/action_service.html +++ b/chromium/chrome/browser/resources/md_downloads/search_service.html @@ -1,4 +1,4 @@ <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://downloads/i18n_setup.html"> <link rel="import" href="chrome://downloads/browser_proxy.html"> -<script src="chrome://downloads/action_service.js"></script> +<script src="chrome://downloads/search_service.js"></script> diff --git a/chromium/chrome/browser/resources/md_downloads/action_service.js b/chromium/chrome/browser/resources/md_downloads/search_service.js index 67e515c6441..991c35289e9 100644 --- a/chromium/chrome/browser/resources/md_downloads/action_service.js +++ b/chromium/chrome/browser/resources/md_downloads/search_service.js @@ -3,8 +3,7 @@ // found in the LICENSE file. cr.define('downloads', function() { - // TODO(dpapad): Rename to SearchService. - class ActionService { + class SearchService { constructor() { /** @private {!Array<string>} */ this.searchTerms_ = []; @@ -48,7 +47,7 @@ cr.define('downloads', function() { * @return {boolean} Whether |searchText| resulted in new search terms. */ search(searchText) { - const searchTerms = ActionService.splitTerms(searchText); + const searchTerms = SearchService.splitTerms(searchText); let sameTerms = searchTerms.length == this.searchTerms_.length; for (let i = 0; sameTerms && i < searchTerms.length; ++i) { @@ -65,7 +64,7 @@ cr.define('downloads', function() { } } - cr.addSingletonGetter(ActionService); + cr.addSingletonGetter(SearchService); - return {ActionService: ActionService}; + return {SearchService: SearchService}; }); diff --git a/chromium/chrome/browser/resources/md_downloads/search_service_unittest.gtestjs b/chromium/chrome/browser/resources/md_downloads/search_service_unittest.gtestjs new file mode 100644 index 00000000000..190c98e3cd8 --- /dev/null +++ b/chromium/chrome/browser/resources/md_downloads/search_service_unittest.gtestjs @@ -0,0 +1,45 @@ +// Copyright 2015 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. + +/** + * @param {!Array<string>} list + * @return {string} + */ +function str(list) { + return JSON.stringify(list); +} + +var SearchServiceUnitTest = class extends testing.Test { + /** @override */ + get extraLibraries() { + return [ + '../../../../ui/webui/resources/js/cr.js', + 'browser_proxy.js', + 'search_service.js', + ]; + } +} + +TEST_F('SearchServiceUnitTest', 'splitTerms', function() { + const SearchService = downloads.SearchService; + assertEquals(str([]), str(SearchService.splitTerms(''))); + assertEquals(str([]), str(SearchService.splitTerms(' '))); + assertEquals(str(['a']), str(SearchService.splitTerms('a'))); + assertEquals(str(['a b']), str(SearchService.splitTerms('a b'))); + assertEquals(str(['a', 'b']), str(SearchService.splitTerms('a "b"'))); + assertEquals(str(['a', 'b', 'c']), str(SearchService.splitTerms('a "b" c'))); + assertEquals(str(['a', 'b b', 'c']), + str(SearchService.splitTerms('a "b b" c'))); +}); + +TEST_F('SearchServiceUnitTest', 'searchWithSimilarTerms', function() { + class TestSearchService extends downloads.SearchService { + loadMore() { /* No chrome.send() for you! */ } + } + + const searchService = new TestSearchService(); + + assertTrue(searchService.search('a')); + assertFalse(searchService.search('a ')); // Same term + space should no-op. +}); diff --git a/chromium/chrome/browser/resources/md_downloads/toolbar.html b/chromium/chrome/browser/resources/md_downloads/toolbar.html index e53051b129c..2985c1d09ed 100644 --- a/chromium/chrome/browser/resources/md_downloads/toolbar.html +++ b/chromium/chrome/browser/resources/md_downloads/toolbar.html @@ -1,6 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://downloads/action_service.html"> +<link rel="import" href="chrome://downloads/search_service.html"> <link rel="import" href="chrome://downloads/browser_proxy.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> diff --git a/chromium/chrome/browser/resources/md_downloads/toolbar.js b/chromium/chrome/browser/resources/md_downloads/toolbar.js index a542050d947..e3a23290305 100644 --- a/chromium/chrome/browser/resources/md_downloads/toolbar.js +++ b/chromium/chrome/browser/resources/md_downloads/toolbar.js @@ -69,9 +69,9 @@ cr.define('downloads', function() { * @private */ onSearchChanged_: function(event) { - const actionService = downloads.ActionService.getInstance(); - if (actionService.search(/** @type {string} */ (event.detail))) - this.spinnerActive = actionService.isSearching(); + const searchService = downloads.SearchService.getInstance(); + if (searchService.search(/** @type {string} */ (event.detail))) + this.spinnerActive = searchService.isSearching(); this.updateClearAll_(); }, diff --git a/chromium/chrome/browser/resources/md_extensions/BUILD.gn b/chromium/chrome/browser/resources/md_extensions/BUILD.gn new file mode 100644 index 00000000000..0d1cd29579f --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/BUILD.gn @@ -0,0 +1,43 @@ +import("../optimize_webui.gni") +import("//tools/grit/grit_rule.gni") +import("//chrome/common/features.gni") + +extensions_pak_file = "extensions_resources.pak" +unpak_folder = "extensions_resources.unpak" + +optimize_webui("build") { + host = "extensions" + html_in_files = [ "extensions.html" ] + html_out_files = [ "vulcanized.html" ] + input = rebase_path("$target_gen_dir/$unpak_folder", root_build_dir) + js_out_files = [ "crisper.js" ] + + deps = [ + ":unpak", + ] +} + +unpak("unpak") { + pak_file = extensions_pak_file + out_folder = unpak_folder + + deps = [ + ":flattened_resources", + ] +} + +grit("flattened_resources") { + source = "extensions_resources.grd" + + # The .grd contains references to generated files. + source_is_generated = true + + defines = chrome_grit_defines + outputs = [ + "grit/extensions_resources.h", + "grit/extensions_resources_map.cc", + "grit/extensions_resources_map.h", + extensions_pak_file, + ] + output_dir = "$root_gen_dir/chrome/browser/resources/md_extensions" +} diff --git a/chromium/chrome/browser/resources/md_extensions/code_section.html b/chromium/chrome/browser/resources/md_extensions/code_section.html index dff22ca8375..e0f7858f689 100644 --- a/chromium/chrome/browser/resources/md_extensions/code_section.html +++ b/chromium/chrome/browser/resources/md_extensions/code_section.html @@ -7,11 +7,22 @@ <dom-module id="extensions-code-section"> <template> <style include="cr-hidden-style"> - #main { + :host { + display: block; + } + + #scroll-container { border: 1px solid var(--paper-grey-500); + height: 100%; + overflow: auto; + position: relative; + } + + #main { color: var(--paper-grey-800); display: flex; font-family: monospace; + min-height: 100%; white-space: pre; } @@ -29,7 +40,7 @@ flex-direction: column; } - .highlight { + #highlight { background: var(--paper-red-100); } @@ -38,18 +49,20 @@ text-align: center; } </style> - <div id="main" hidden$="[[isEmpty(code)]]"> - <div id="line-numbers"> - <span>[[computeLineNumbersContent_(code.*)]]</span> - </div> - <div id="source"> - <span>[[code.beforeHighlight]]<!-- No whitespace allowed - --><span class="highlight" title="[[code.message]]"><!-- - -->[[code.highlight]]<!-- - --></span>[[code.afterHighlight]]</span> + <div id="scroll-container" hidden$="[[isEmpty(code)]]"> + <div id="main"> + <div id="line-numbers"> + <span>[[computeLineNumbersContent_(code.*)]]</span> + </div> + <div id="source"> + <span>[[code.beforeHighlight]]<!-- No whitespace allowed + --><span id="highlight" title="[[code.message]]"><!-- + -->[[code.highlight]]<!-- + --></span>[[code.afterHighlight]]</span> + </div> </div> </div> <div id="no-code" hidden$="[[!isEmpty(code)]]">[[couldNotDisplayCode]]</div> </template> - <script src="chrome://extensions/code_section.js"></script> + <script src="code_section.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/code_section.js b/chromium/chrome/browser/resources/md_extensions/code_section.js index 1ea32d783d0..d2fa2caab58 100644 --- a/chromium/chrome/browser/resources/md_extensions/code_section.js +++ b/chromium/chrome/browser/resources/md_extensions/code_section.js @@ -28,6 +28,10 @@ cr.define('extensions', function() { couldNotDisplayCode: String, }, + observers: [ + 'onHighlightChanged_(code.highlight)', + ], + /** * Returns true if no code could be displayed (e.g. because the file could * not be loaded). @@ -58,6 +62,15 @@ cr.define('extensions', function() { textContent += i + '\n'; return textContent; }, + + /** @private */ + onHighlightChanged_: function() { + // Smooth scroll the highlight to roughly the middle. + this.$['scroll-container'].scrollTo({ + top: this.$.highlight.offsetTop - this.clientHeight * 0.5, + behavior: 'smooth', + }); + }, }); return {CodeSection: CodeSection}; diff --git a/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp b/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp index 7dc433766ce..7689bd4eb76 100644 --- a/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp @@ -14,6 +14,7 @@ { 'target_name': 'detail_view', 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:cr_container_shadow_behavior', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', @@ -26,18 +27,28 @@ 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { + 'target_name': 'drag_and_drop_handler', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:drag_wrapper', + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'drop_overlay', 'dependencies': [ - '<(DEPTH)/chrome/browser/resources/extensions/compiled_resources2.gyp:drag_and_drop_handler', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:drag_wrapper', + 'drag_and_drop_handler', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { 'target_name': 'error_page', 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:cr_container_shadow_behavior', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-menu/compiled_resources2.gyp:paper-menu-extracted', '<(EXTERNS_GYP):developer_private', @@ -72,10 +83,10 @@ { 'target_name': 'item_list', 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:cr_container_shadow_behavior', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', - '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/compiled_resources2.gyp:iron-resizable-behavior-extracted', '<(EXTERNS_GYP):developer_private', 'item', ], @@ -95,6 +106,7 @@ { 'target_name': 'keyboard_shortcuts', 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:cr_container_shadow_behavior', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(EXTERNS_GYP):developer_private', @@ -102,6 +114,22 @@ 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { + 'target_name': 'kiosk_browser_proxy', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'kiosk_dialog', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', + 'kiosk_browser_proxy', + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'load_error', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', @@ -124,6 +152,7 @@ 'item_util', 'load_error', 'keyboard_shortcuts', + 'kiosk_browser_proxy', 'navigation_helper', 'sidebar', 'toolbar', @@ -136,6 +165,7 @@ 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -152,7 +182,6 @@ { 'target_name': 'pack_dialog', 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(EXTERNS_GYP):developer_private', ], @@ -163,6 +192,7 @@ 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(EXTERNS_GYP):developer_private', '<(EXTERNS_GYP):management', 'error_page', @@ -178,11 +208,19 @@ { 'target_name': 'shortcut_input', 'dependencies': [ - '<(DEPTH)/chrome/browser/resources/extensions/compiled_resources2.gyp:shortcut_util', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', '<(EXTERNS_GYP):developer_private', + 'shortcut_util', + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'shortcut_util', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -191,7 +229,6 @@ 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', 'navigation_helper', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], @@ -199,7 +236,6 @@ { 'target_name': 'toolbar', 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', ], diff --git a/chromium/chrome/browser/resources/md_extensions/detail_view.html b/chromium/chrome/browser/resources/md_extensions/detail_view.html index 3a23f8a31c0..c66a6a5eb6d 100644 --- a/chromium/chrome/browser/resources/md_extensions/detail_view.html +++ b/chromium/chrome/browser/resources/md_extensions/detail_view.html @@ -1,9 +1,11 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html"> -<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> -<link rel="import" href="chrome://resources/cr_elements/paper_toggle_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_tooltip_icon.html"> <link rel="import" href="chrome://resources/html/action_link.html"> <link rel="import" href="chrome://resources/html/action_link_css.html"> @@ -13,18 +15,18 @@ <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.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/paper-styles/color.html"> -<link rel="import" href="chrome://extensions/item_util.html"> -<link rel="import" href="chrome://extensions/navigation_helper.html"> +<link rel="import" href="strings.html"> +<link rel="import" href="item_util.html"> +<link rel="import" href="navigation_helper.html"> <dom-module id="extensions-detail-view"> <template> - <style include= - "iron-flex cr-hidden-style cr-icons action-link paper-toggle-style"> + <style include="iron-flex cr-shared-style cr-icons action-link + paper-button-style"> :host { --iron-icon-fill-color: var(--paper-grey-600); display: block; height: 100%; - overflow: auto; } #enable-section { @@ -35,6 +37,11 @@ -webkit-margin-end: 20px; } + #container { + height: 100%; + overflow: overlay; + } + #main { background-color: white; margin: auto; @@ -44,9 +51,7 @@ #top-bar { align-items: center; - color: var(--paper-grey-700); display: flex; - font-size: 14px; height: 40px; margin-bottom: 12px; padding: 8px 12px 0; @@ -61,6 +66,7 @@ #name { flex-grow: 1; + @apply(--cr-title-text); } .control-line { @@ -88,6 +94,11 @@ border-top: none; } + .section.continuation.warning { + padding-left: 21px; + padding-right: 8px; + } + .section:first-child { border: none; } @@ -111,146 +122,210 @@ width: auto; /* override the default button size of 24x24 */ } + .warning .action-button { + background: white; + color: var(--google-blue-500); + margin-left: 14px; + width: 78px; + } + + .warning div { + display: flex; + } + + .warning span { + color: var(--paper-red-700); + width: 484px; + } + + .warning-icon { + --iron-icon-fill-color: var(--paper-red-700); + --iron-icon-height: 19px; + --iron-icon-width: 19px; + margin-right: 16px; + } + ul { -webkit-padding-start: 20px; margin: 0; } + + #remove-extension { + width: 100%; + } </style> - <div id="main"> - <div id="top-bar"> - <button id="close-button" is="paper-icon-button-light" - class="icon-arrow-back no-overlap" - on-tap="onCloseButtonTap_"></button> - <img alt="" id="icon" src="[[data.iconUrl]]"> - <span id="name">[[data.name]]</span> - </div> - <div class="section continuation" id="enable-section"> - <div class="control-line"> - <span>[[computeEnabledText_(data.*)]]</span> - <div class="layout horizontal"> - <cr-tooltip-icon hidden$="[[!data.controlledInfo]]" - tooltip-text="[[data.controlledInfo.text]]" - icon-class="[[getIndicatorIcon_(data.controlledInfo.type)]]" - icon-aria-label="[[data.controlledInfo.type]]"> - </cr-tooltip-icon> - <paper-toggle-button id="enable-toggle" - checked="[[isEnabled_(data.state)]]" - on-change="onEnableChange_" - disabled="[[!isEnableToggleEnabled_(data.*)]]"> - </paper-toggle-button> + <div id="container"> + <div id="main"> + <div id="top-bar"> + <button id="close-button" is="paper-icon-button-light" + class="icon-arrow-back no-overlap" + on-tap="onCloseButtonTap_"></button> + <img alt="" id="icon" src="[[data.iconUrl]]"> + <span id="name">[[data.name]]</span> + </div> + <div class="section continuation" id="enable-section"> + <div class="control-line"> + <span>[[computeEnabledText_(data.*)]]</span> + <div class="layout horizontal"> + <cr-tooltip-icon hidden$="[[!data.controlledInfo]]" + tooltip-text="[[data.controlledInfo.text]]" + icon-class="[[getIndicatorIcon_(data.controlledInfo.type)]]" + icon-aria-label="[[data.controlledInfo.type]]"> + </cr-tooltip-icon> + <cr-toggle id="enable-toggle" + checked="[[isEnabled_(data.state)]]" + on-change="onEnableChange_" + disabled="[[!isEnableToggleEnabled_(data.*)]]"> + </cr-toggle> + </div> </div> </div> - </div> - <div class="section continuation block"> - <div class="section-title">$i18n{itemDescriptionLabel}</div> - <div class="section-content">[[data.description]]</div> - </div> - <div class="section block"> - <div class="section-title">$i18n{itemVersion}</div> - <div class="section-content">[[data.version]]</div> - </div> - <div class="section block" id="id-section" hidden$="[[!inDevMode]]"> - <div class="section-title">$i18n{itemIdHeading}</div> - <div class="section-content">[[data.id]]</div> - </div> - <div class="section" id="inspectable-views" hidden$="[[!inDevMode]]"> - <div class="section-title">$i18n{itemInspectViews}</div> - <div class="section-content"> - <ul id="inspect-views"> - <template is="dom-repeat" items="[[data.views]]"> - <li> - <a is="action-link" class="inspectable-view" - on-tap="onInspectTap_"> - [[computeInspectLabel_(item)]] - </a> - </li> - </template> - </ul> + <div id="warnings" hidden$="[[!hasWarnings_(data.*)]]"> + <div class="section continuation warning" id="suspicious-warning" + hidden$="[[!data.disableReasons.suspiciousInstall]]"> + <div> + <iron-icon class="warning-icon" icon="cr:warning"></iron-icon> + <span>$i18n{itemSuspiciousInstall}</span> + </div> + </div> + <div class="section continuation warning" id="corrupted-warning" + hidden$="[[!data.disableReasons.corruptInstall]]"> + <div> + <iron-icon class="warning-icon" icon="cr:warning"></iron-icon> + <span>$i18n{itemCorruptInstall}</span> + </div> + <paper-button id="repair-button" class="action-button" + on-tap="onRepairTap_"> + $i18n{itemRepair} + </paper-button> + </div> + <div class="section continuation warning" id="blacklisted-warning" + hidden$="[[!data.blacklistText]]"> + <div> + <iron-icon class="warning-icon" icon="cr:warning"></iron-icon> + <span>[[data.blacklistText]]</span> + </div> + </div> + <div class="section continuation warning" id="update-required-warning" + hidden$="[[!data.disableReasons.updateRequired]]"> + <div> + <iron-icon class="warning-icon" icon="cr:warning"></iron-icon> + <span>$i18n{updateRequiredByPolicy}</span> + </div> + </div> </div> - </div> - <div class="section block"> - <div class="section-title">$i18n{itemPermissions}</div> - <div class="section-content"> - <span id="no-permissions" - hidden$="[[hasPermissions_(data.permissions.splices)]]"> - $i18n{itemPermissionsEmpty} - </span> - <ul id="permissions-list" - hidden$="[[!hasPermissions_(data.permissions.splices)]]"> - <template is="dom-repeat" items="[[data.permissions]]"> - <li>[[item]]</li> - </template> - </ul> + <div class="section continuation block"> + <div class="section-title">$i18n{itemDescriptionLabel}</div> + <div class="section-content">[[data.description]]</div> </div> - </div> - <template is="dom-if" - if="[[hasDependentExtensions_(data.dependentExtensions.splices)]]"> <div class="section block"> - <div class="section-title">$i18n{itemDependencies}</div> + <div class="section-title">$i18n{itemVersion}</div> + <div class="section-content">[[data.version]]</div> + </div> + <div class="section block" id="id-section" hidden$="[[!inDevMode]]"> + <div class="section-title">$i18n{itemIdHeading}</div> + <div class="section-content">[[data.id]]</div> + </div> + <div class="section" id="inspectable-views" hidden$="[[!inDevMode]]"> + <div class="section-title">$i18n{itemInspectViews}</div> <div class="section-content"> - <ul id="dependent-extensions-list"> - <template is="dom-repeat" items="[[data.dependentExtensions]]"> - <li>[[computeDependentEntry_(item)]]</li> + <ul id="inspect-views"> + <template is="dom-repeat" items="[[data.views]]"> + <li> + <a is="action-link" class="inspectable-view" + on-tap="onInspectTap_"> + [[computeInspectLabel_(item)]] + </a> + </li> </template> </ul> </div> </div> - </template> - <template is="dom-if" if="[[shouldShowOptionsSection_(data.*)]]"> - <div class="section"> - <template is="dom-if" if="[[data.incognitoAccess.isEnabled]]"> - <div class="control-line"> - <span>$i18n{itemAllowIncognito}</span> - <paper-toggle-button id="allow-incognito" - checked="[[data.incognitoAccess.isActive]]" - on-change="onAllowIncognitoChange_"></paper-toggle-button> - </div> - </template> - <template is="dom-if" if="[[data.fileAccess.isEnabled]]"> - <div class="control-line"> - <span>$i18n{itemAllowOnFileUrls}</span> - <paper-toggle-button id="allow-on-file-urls" - checked="[[data.fileAccess.isActive]]" - on-change="onAllowOnFileUrlsChange_"></paper-toggle-button> - </div> - </template> - <template is="dom-if" if="[[data.runOnAllUrls.isEnabled]]"> - <div class="control-line"> - <span>$i18n{itemAllowOnAllSites}</span> - <paper-toggle-button id="allow-on-all-sites" - checked="[[data.runOnAllUrls.isActive]]" - on-change="onAllowOnAllSitesChange_"></paper-toggle-button> - </div> - </template> - <template is="dom-if" if="[[data.errorCollection.isEnabled]]"> - <div class="control-line"> - <span>$i18n{itemCollectErrors}</span> - <paper-toggle-button id="collect-errors" - checked="[[data.errorCollection.isActive]]" - on-change="onCollectErrorsChange_"></paper-toggle-button> - </div> - </template> + <div class="section block"> + <div class="section-title">$i18n{itemPermissions}</div> + <div class="section-content"> + <span id="no-permissions" + hidden$="[[hasPermissions_(data.permissions.splices)]]"> + $i18n{itemPermissionsEmpty} + </span> + <ul id="permissions-list" + hidden$="[[!hasPermissions_(data.permissions.splices)]]"> + <template is="dom-repeat" items="[[data.permissions]]"> + <li>[[item]]</li> + </template> + </ul> + </div> </div> - </template> - <div class="section" - hidden$="[[!shouldShowOptionsLink_(data.*)]]"> - <div class="control-line actionable" id="extensions-options" - on-tap="onOptionsTap_"> - <span>$i18n{itemOptions}</span> - <button class="icon-external" is="paper-icon-button-light"></button> + <template is="dom-if" + if="[[hasDependentExtensions_(data.dependentExtensions.splices)]]"> + <div class="section block"> + <div class="section-title">$i18n{itemDependencies}</div> + <div class="section-content"> + <ul id="dependent-extensions-list"> + <template is="dom-repeat" items="[[data.dependentExtensions]]"> + <li>[[computeDependentEntry_(item)]]</li> + </template> + </ul> + </div> + </div> + </template> + <template is="dom-if" if="[[shouldShowOptionsSection_(data.*)]]"> + <div class="section"> + <template is="dom-if" if="[[data.incognitoAccess.isEnabled]]"> + <div class="control-line"> + <span>$i18n{itemAllowIncognito}</span> + <cr-toggle id="allow-incognito" + checked="[[data.incognitoAccess.isActive]]" + on-change="onAllowIncognitoChange_"></cr-toggle> + </div> + </template> + <template is="dom-if" if="[[data.fileAccess.isEnabled]]"> + <div class="control-line"> + <span>$i18n{itemAllowOnFileUrls}</span> + <cr-toggle id="allow-on-file-urls" + checked="[[data.fileAccess.isActive]]" + on-change="onAllowOnFileUrlsChange_"></cr-toggle> + </div> + </template> + <template is="dom-if" if="[[data.runOnAllUrls.isEnabled]]"> + <div class="control-line"> + <span>$i18n{itemAllowOnAllSites}</span> + <cr-toggle id="allow-on-all-sites" + checked="[[data.runOnAllUrls.isActive]]" + on-change="onAllowOnAllSitesChange_"></cr-toggle> + </div> + </template> + <template is="dom-if" if="[[data.errorCollection.isEnabled]]"> + <div class="control-line"> + <span>$i18n{itemCollectErrors}</span> + <cr-toggle id="collect-errors" + checked="[[data.errorCollection.isActive]]" + on-change="onCollectErrorsChange_"></cr-toggle> + </div> + </template> + </div> + </template> + <div class="section" + hidden$="[[!shouldShowOptionsLink_(data.*)]]"> + <div class="control-line actionable" id="extensions-options" + on-tap="onOptionsTap_"> + <span>$i18n{itemOptions}</span> + <button class="icon-external" is="paper-icon-button-light"></button> + </div> </div> - </div> - <button is="cr-link-row" hidden="[[isControlled_(data.controlledInfo)]]" - icon-class="subpage-arrow" id="remove-extension" - label="$i18n{itemRemoveExtension}" on-tap="onRemoveTap_"> - </button> - <div class="section block"> - <div class="section-title">$i18n{itemSource}</div> - <div class="section-content"> - [[computeSourceString_(data.*)]] + <button class="hr" is="cr-link-row" + hidden="[[isControlled_(data.controlledInfo)]]" + icon-class="subpage-arrow" id="remove-extension" + label="$i18n{itemRemoveExtension}" on-tap="onRemoveTap_"> + </button> + <div class="section block"> + <div class="section-title">$i18n{itemSource}</div> + <div class="section-content"> + [[computeSourceString_(data.*)]] + </div> </div> </div> </div> </template> - <script src="chrome://extensions/detail_view.js"></script> + <script src="detail_view.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/detail_view.js b/chromium/chrome/browser/resources/md_extensions/detail_view.js index 0ea6870e448..9f7ab51e2db 100644 --- a/chromium/chrome/browser/resources/md_extensions/detail_view.js +++ b/chromium/chrome/browser/resources/md_extensions/detail_view.js @@ -8,7 +8,7 @@ cr.define('extensions', function() { const DetailView = Polymer({ is: 'extensions-detail-view', - behaviors: [I18nBehavior], + behaviors: [I18nBehavior, CrContainerShadowBehavior], properties: { /** @@ -71,6 +71,16 @@ cr.define('extensions', function() { }, /** + * @return {boolean} + * @private + */ + hasWarnings_: function() { + return this.data.disableReasons.corruptInstall || + this.data.disableReasons.suspiciousInstall || + this.data.disableReasons.updateRequired || !!this.data.blacklistText; + }, + + /** * @return {string} * @private */ @@ -143,6 +153,11 @@ cr.define('extensions', function() { }, /** @private */ + onRepairTap_: function() { + this.delegate.repairItem(this.data.id); + }, + + /** @private */ onAllowIncognitoChange_: function() { this.delegate.setItemAllowedIncognito( this.data.id, this.$$('#allow-incognito').checked); diff --git a/chromium/chrome/browser/resources/md_extensions/drag_and_drop_handler.html b/chromium/chrome/browser/resources/md_extensions/drag_and_drop_handler.html new file mode 100644 index 00000000000..2626f1e7a3f --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/drag_and_drop_handler.html @@ -0,0 +1,2 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="drag_and_drop_handler.js"></script> diff --git a/chromium/chrome/browser/resources/extensions/drag_and_drop_handler.js b/chromium/chrome/browser/resources/md_extensions/drag_and_drop_handler.js index 9643f5ee222..f79bb43cde1 100644 --- a/chromium/chrome/browser/resources/extensions/drag_and_drop_handler.js +++ b/chromium/chrome/browser/resources/md_extensions/drag_and_drop_handler.js @@ -34,7 +34,7 @@ cr.define('extensions', function() { // not. // See: http://www.w3.org/TR/2011/WD-html5-20110113/dnd.html#concept-dnd-p return !!e.dataTransfer.types && - e.dataTransfer.types.indexOf('Files') > -1; + e.dataTransfer.types.indexOf('Files') > -1; }, /** @override */ @@ -61,10 +61,10 @@ cr.define('extensions', function() { if (e.dataTransfer.files.length != 1) return; - var toSend = ''; + let toSend = ''; // Files lack a check if they're a directory, but we can find out through // its item entry. - for (var i = 0; i < e.dataTransfer.items.length; ++i) { + for (let i = 0; i < e.dataTransfer.items.length; ++i) { if (e.dataTransfer.items[i].kind == 'file' && e.dataTransfer.items[i].webkitGetAsEntry().isDirectory) { toSend = 'installDroppedDirectory'; @@ -84,7 +84,7 @@ cr.define('extensions', function() { } }, - /** @private */ + /** @private */ fireDragEnded_: function() { this.eventTarget_.dispatchEvent(new CustomEvent('extension-drag-ended')); } diff --git a/chromium/chrome/browser/resources/md_extensions/drop_overlay.html b/chromium/chrome/browser/resources/md_extensions/drop_overlay.html index 5e22a036e60..7c6c510578f 100644 --- a/chromium/chrome/browser/resources/md_extensions/drop_overlay.html +++ b/chromium/chrome/browser/resources/md_extensions/drop_overlay.html @@ -3,10 +3,9 @@ <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/cr/ui/drag_wrapper.html"> -<link rel="import" href="chrome://resources/html/load_time_data.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html"> -<link rel="import" href="chrome://extensions/drag_and_drop_handler.html"> +<link rel="import" href="drag_and_drop_handler.html"> <dom-module id="extensions-drop-overlay"> <template> @@ -40,5 +39,5 @@ <div>$i18n{dropToInstall}</div> </div> </template> - <script src="chrome://extensions/drop_overlay.js"></script> + <script src="drop_overlay.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/error_page.html b/chromium/chrome/browser/resources/md_extensions/error_page.html index b3103aaa277..0ddb9486ee4 100644 --- a/chromium/chrome/browser/resources/md_extensions/error_page.html +++ b/chromium/chrome/browser/resources/md_extensions/error_page.html @@ -1,46 +1,75 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> -<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> <link rel="import" href="chrome://resources/html/cr.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-icons/iron-icons.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-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="chrome://resources/polymer/v1_0/paper-styles/color.html"> -<link rel="import" href="chrome://extensions/code_section.html"> -<link rel="import" href="chrome://extensions/item_util.html"> -<link rel="import" href="chrome://extensions/navigation_helper.html"> +<link rel="import" href="code_section.html"> +<link rel="import" href="item_util.html"> +<link rel="import" href="navigation_helper.html"> <dom-module id="extensions-error-page"> <template> - <style include="cr-icons cr-hidden-style"> + <style include="paper-button-style cr-icons cr-shared-style"> + :host { + display: block; + height: 100%; + } + + #container { + height: 100%; + overflow: overlay; + } + #main { background-color: white; - height: 800px; - width: 90%; + margin: auto; + min-height: 100%; + width: 640px; + } + + .section { + padding: 0 var(--cr-section-padding); } #heading { - color: var(--paper-grey-600); height: 40px; margin-bottom: 30px; padding: 8px 12px 0; + @apply(--cr-title-text); } #errors-list { min-height: 100px; - padding: 0 10px; } .error-item { - align-items: center; - color: var(--paper-grey-800); - display: flex; - margin: 5px 0; + @apply(--cr-section); + padding-left: 0; + } + + .error-item button[is='paper-icon-button-light'] { + -webkit-margin-end: 0; } .error-item.selected { - background-color: #ccc; + background-color: rgba(0, 0, 0, 0.08); + } + + .error-item .start { + align-items: center; + align-self: stretch; /* Makes the tappable area fill its parent. */ + display: flex; + flex: 1; + padding: 0 var(--cr-section-padding); } .error-message { @@ -60,36 +89,128 @@ .icon-severity-fatal { content: url(error_severity_fatal.png); } + + #devtools-controls { + padding: 0 var(--cr-section-padding); + } + + #stack-trace-heading { + @apply(--cr-title-text); + align-items: center; + display: flex; + height: var(--cr-section-min-height); + } + + #stack-trace-container { + list-style: none; + margin-top: 0; + padding: 0; + } + + #stack-trace-container li { + cursor: pointer; + font-family: monospace; + padding: 4px; + } + + #stack-trace-container li.selected, + #stack-trace-container li:hover { + background: var(--google-blue-100); + } + + #dev-tool-button { + margin-bottom: 20px; + max-width: 300px; + } + + extensions-code-section { + background: white; + height: 200px; + } + + /* TODO(scottchen): extract to shared location from settings. */ + .separator { + -webkit-border-start: var(--cr-separator-line); + -webkit-margin-end: var(--cr-section-padding); + -webkit-margin-start: var(--cr-section-padding); + flex-shrink: 0; + --separator-gaps: 9px; + height: calc(var(--cr-section-min-height) - + var(--separator-gaps)); + + /** + * Makes the tappable area fill its parent. + * TODO(scottchen): This is an explicit reminder to override once + * .separator styling is extracted from settings. + */ + -webkit-margin-start: 0; + } + + /* TODO(scottchen): extract to shared location from settings. */ + .separator + button[is='paper-icon-button-light'] { + -webkit-margin-start: var(--cr-icon-ripple-margin); + } </style> - <div id="main"> - <div id="heading"> - <button id="close-button" is="paper-icon-button-light" - class="icon-arrow-back no-overlap" on-tap="onCloseButtonTap_"> - </button> - <span>$i18n{errorsPageHeading}</span> - </div> - <iron-list id="errors-list" items="[[calculateShownItems_(data.*)]]"> - <template> - <div class$="[[computeErrorClass_(selectedError_, item)]]" - error="[[item]]" - tabindex$="[[tabIndex]]" - on-tap="onErrorItemTap_" on-keydown="onErrorItemKeydown_"> - <img class$="[[computeErrorIconClass_(item)]]"> - <div class="error-message">[[item.message]]</div> - <button is="paper-icon-button-light" class="icon-delete-gray" - on-tap="onDeleteErrorTap_" tabindex$="[[tabIndex]]"></button> - </div> - </template> - </iron-list> - <div id="content-view"> - <extensions-code-section id="code-section" - could-not-display-code="$i18n{noErrorsToShow}"> - </extensions-code-section> - </div> - <div id="devtools-controls"> - <!--TODO--> + <div id="container"> + <div id="main"> + <div id="heading"> + <button id="close-button" is="paper-icon-button-light" + class="icon-arrow-back no-overlap" on-tap="onCloseButtonTap_"> + </button> + <span>$i18n{errorsPageHeading}</span> + </div> + <div class="section"> + <extensions-code-section id="code-section" + could-not-display-code="$i18n{noErrorsToShow}"> + </extensions-code-section> + <div id="errors-list"> + <template is="dom-repeat" items="[[calculateShownItems_(data.*)]]"> + <div class="item-container"> + <div class$="error-item [[computeErrorClass_(selectedError_, item)]]"> + <div class="start" on-tap="onErrorItemAction_" + on-keydown="onErrorItemAction_" tabindex="0"> + <img class$="[[computeErrorIconClass_(item)]]"> + <div class="error-message">[[item.message]]</div> + <cr-expand-button hidden="[[!computeIsRuntimeError_(item)]]" + expanded="[[isEqual_(selectedError_, item)]]" + tab-index="-1"> + </cr-expand-button> + </div> + <div class="separator"></div> + <button is="paper-icon-button-light" class="icon-delete-gray" + on-tap="onDeleteErrorAction_" + on-keydown="onDeleteErrorAction_"> + </button> + </div> + <template is="dom-if" if="[[computeIsRuntimeError_(item)]]"> + <iron-collapse opened="[[isEqual_(selectedError_, item)]]"> + <div id="devtools-controls"> + <div id="stack-trace-heading"> + $i18n{stackTrace} + </div> + <ul id="stack-trace-container"> + <template is="dom-repeat" items="[[item.stackTrace]]"> + <li on-tap="onStackFrameTap_" + hidden="[[!shouldDisplayFrame_(item.url)]]" + class$="[[getStackFrameClass_(item, selectedStackFrame_)]]"> + [[getStackTraceLabel_(item)]] + </li> + </template> + </ul> + <paper-button id="dev-tool-button" class="action-button" + disabled="[[!item.canInspect]]" + on-tap="onDevToolButtonTap_"> + $i18n{openInDevtool} + </paper-button> + </div> + </iron-collapse> + </template> + </div> + </template> + </iron-list> + </div> </div> </div> </template> - <script src="chrome://extensions/error_page.js"></script> + <script src="error_page.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/error_page.js b/chromium/chrome/browser/resources/md_extensions/error_page.js index 01bcf79e3cf..113561d6018 100644 --- a/chromium/chrome/browser/resources/md_extensions/error_page.js +++ b/chromium/chrome/browser/resources/md_extensions/error_page.js @@ -11,26 +11,43 @@ cr.define('extensions', function() { 'use strict'; /** @interface */ - const ErrorPageDelegate = function() {}; - - ErrorPageDelegate.prototype = { + class ErrorPageDelegate { /** * @param {string} extensionId - * @param {!Array<number>=} opt_errorIds - * @param {chrome.developerPrivate.ErrorType=} opt_type + * @param {!Array<number>=} errorIds + * @param {chrome.developerPrivate.ErrorType=} type */ - deleteErrors: assertNotReached, + deleteErrors(extensionId, errorIds, type) {} /** * @param {chrome.developerPrivate.RequestFileSourceProperties} args * @return {!Promise<!chrome.developerPrivate.RequestFileSourceResponse>} */ - requestFileSource: assertNotReached, - }; + requestFileSource(args) {} + + /** + * @param {!chrome.developerPrivate.OpenDevToolsProperties} args + */ + openDevTools(args) {} + } + + /** + * Get the URL relative to the main extension url. If the url is + * unassociated with the extension, this will be the full url. + * @param {string} url + * @param {?(ManifestError|RuntimeError)} error + * @return {string} + */ + function getRelativeUrl(url, error) { + const fullUrl = 'chrome-extension://' + error.extensionId + '/'; + return url.startsWith(fullUrl) ? url.substring(fullUrl.length) : url; + } const ErrorPage = Polymer({ is: 'extensions-error-page', + behaviors: [CrContainerShadowBehavior], + properties: { /** @type {!chrome.developerPrivate.ExtensionInfo|undefined} */ data: Object, @@ -40,6 +57,14 @@ cr.define('extensions', function() { /** @private {?(ManifestError|RuntimeError)} */ selectedError_: Object, + + /** @private {?chrome.developerPrivate.StackFrame}*/ + selectedStackFrame_: { + type: Object, + value: function() { + return null; + }, + }, }, observers: [ @@ -54,9 +79,8 @@ cr.define('extensions', function() { */ observeDataChanges_: function() { assert(this.data); - const e = this.data.manifestErrors[0] || this.data.runtimeErrors[0]; - if (e) - this.selectedError_ = e; + this.selectedError_ = + this.data.manifestErrors[0] || this.data.runtimeErrors[0] || null; }, /** @@ -98,14 +122,16 @@ cr.define('extensions', function() { }, /** - * @param {!Event} event + * @param {!Event} e * @private */ - onDeleteErrorTap_: function(event) { - // TODO(devlin): It would be cleaner if we could cast this to a - // PolymerDomRepeatEvent-type thing, but that doesn't exist yet. - const e = /** @type {!{model:Object}} */ (event); - this.delegate.deleteErrors(this.data.id, [e.model.item.id]); + onDeleteErrorAction_: function(e) { + if (e.type == 'keydown' && !((e.code == 'Space' || e.code == 'Enter'))) + return; + + this.delegate.deleteErrors( + this.data.id, [(/** @type {!{model:Object}} */ (e)).model.item.id]); + e.stopPropagation(); }, /** @@ -113,6 +139,11 @@ cr.define('extensions', function() { * @private */ onSelectedErrorChanged_: function() { + if (!this.selectedError_) { + this.$['code-section'].code = null; + return; + } + const error = this.selectedError_; const args = { extensionId: error.extensionId, @@ -130,6 +161,9 @@ cr.define('extensions', function() { args.lineNumber = error.stackTrace && error.stackTrace[0] ? error.stackTrace[0].lineNumber : 0; + this.selectedStackFrame_ = error.stackTrace && error.stackTrace[0] ? + error.stackTrace[0] : + null; break; } this.delegate.requestFileSource(args).then(code => { @@ -138,6 +172,100 @@ cr.define('extensions', function() { }, /** + * @return {boolean} + * @private + */ + computeIsRuntimeError_: function(item) { + return item.type == chrome.developerPrivate.ErrorType.RUNTIME; + }, + + /** + * The description is a human-readable summation of the frame, in the + * form "<relative_url>:<line_number> (function)", e.g. + * "myfile.js:25 (myFunction)". + * @param {!chrome.developerPrivate.StackFrame} frame + * @return {string} + * @private + */ + getStackTraceLabel_: function(frame) { + let description = getRelativeUrl(frame.url, this.selectedError_) + ':' + + frame.lineNumber; + + if (frame.functionName) { + const functionName = frame.functionName == '(anonymous function)' ? + loadTimeData.getString('anonymousFunction') : + frame.functionName; + description += ' (' + functionName + ')'; + } + + return description; + }, + + /** @private */ + getExpandedClass_: function() { + return this.stackTraceExpanded_ ? 'expanded' : ''; + }, + + /** + * @param {chrome.developerPrivate.StackFrame} frame + * @return {string} + * @private + */ + getStackFrameClass_: function(frame) { + return frame == this.selectedStackFrame_ ? 'selected' : ''; + }, + + /** + * This function is used to determine whether or not we want to show a + * stack frame. We don't want to show code from internal scripts. + * @param {string} url + * @return {boolean} + * @private + */ + shouldDisplayFrame_: function(url) { + // All our internal scripts are in the 'extensions::' namespace. + return !/^extensions::/.test(url); + }, + + /** + * @param {!Event} e + * @private + */ + onStackFrameTap_: function(e) { + const frame = (/** @type {!{model:Object}} */ (e)).model.item; + + this.selectedStackFrame_ = frame; + + this.delegate + .requestFileSource({ + extensionId: this.selectedError_.extensionId, + message: this.selectedError_.message, + pathSuffix: getRelativeUrl(frame.url, this.selectedError_), + lineNumber: frame.lineNumber, + }) + .then(code => { + this.$['code-section'].code = code; + }); + }, + + /** @private */ + onDevToolButtonTap_: function() { + // This guarantees renderProcessId and renderViewId. + assert( + this.selectedError_.type == + chrome.developerPrivate.ErrorType.RUNTIME); + assert(this.selectedStackFrame_); + + this.delegate.openDevTools({ + renderProcessId: this.selectedError_.renderProcessId, + renderViewId: this.selectedError_.renderViewId, + url: this.selectedStackFrame_.url, + lineNumber: this.selectedStackFrame_.lineNumber || 0, + columnNumber: this.selectedStackFrame_.columnNumber || 0, + }); + }, + + /** * Computes the class name for the error item depending on whether its * the currently selected error. * @param {!RuntimeError|!ManifestError} selectedError @@ -146,33 +274,37 @@ cr.define('extensions', function() { * @private */ computeErrorClass_: function(selectedError, error) { - return selectedError == error ? 'error-item selected' : 'error-item'; + return selectedError == error ? 'selected' : ''; }, /** - * Causes the given error to become the currently-selected error. - * @param {!RuntimeError|!ManifestError} error + * @param {!RuntimeError|!ManifestError} selected + * @param {!RuntimeError|!ManifestError} current + * @return {boolean} * @private */ - selectError_: function(error) { - this.selectedError_ = error; + isEqual_: function(selected, current) { + return selected == current; }, /** - * @param {!{model: !{item: (!RuntimeError|!ManifestError)}}} e + * Causes the given error to become the currently-selected error. + * @param {!RuntimeError|!ManifestError} error * @private */ - onErrorItemTap_: function(e) { - this.selectError_(e.model.item); + selectError_: function(error) { + this.selectedError_ = error; }, /** * @param {!{model: !{item: (!RuntimeError|!ManifestError)}}} e * @private */ - onErrorItemKeydown_: function(e) { - if (e.key == ' ' || e.key == 'Enter') - this.selectError_(e.model.item); + onErrorItemAction_: function(e) { + if (e.type == 'keydown' && !((e.code == 'Space' || e.code == 'Enter'))) + return; + + this.selectError_(e.model.item); }, }); diff --git a/chromium/chrome/browser/resources/md_extensions/extensions.html b/chromium/chrome/browser/resources/md_extensions/extensions.html index fb287b7b84f..1428eb4e94f 100644 --- a/chromium/chrome/browser/resources/md_extensions/extensions.html +++ b/chromium/chrome/browser/resources/md_extensions/extensions.html @@ -3,8 +3,11 @@ <head> <meta charset="utf8"> <title>$i18n{title}</title> +<if expr="not optimize_webui"> + <base href="chrome://extensions"> +</if> <link rel="import" href="chrome://resources/html/polymer.html"> - <link rel="import" href="chrome://extensions/manager.html"> + <link rel="import" href="manager.html"> <link rel="stylesheet" href="chrome://resources/css/md_colors.css"> <style> html { @@ -16,15 +19,15 @@ height: 100%; line-height: 154%; margin: 0; + overflow: hidden; + width: 100%; } </style> </head> <body> <extensions-manager></extensions-manager> <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> - <link rel="import" href="chrome://resources/html/load_time_data.html"> - <link rel="import" href="chrome://extensions/strings.html"> - <link rel="import" href="chrome://extensions/service.html"> - <script src="chrome://extensions/extensions.js"></script> + <link rel="import" href="service.html"> + <script src="extensions.js"></script> </body> </html> diff --git a/chromium/chrome/browser/resources/md_extensions/extensions_resources.grd b/chromium/chrome/browser/resources/md_extensions/extensions_resources.grd new file mode 100644 index 00000000000..c8d179899b3 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/extensions_resources.grd @@ -0,0 +1,179 @@ +<?xml version="1.0" encoding="UTF-8"?> +<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> + <outputs> + <output filename="grit/extensions_resources.h" type="rc_header"> + <emit emit_type='prepend'></emit> + </output> + <output filename="grit/extensions_resources_map.cc" + type="resource_file_map_source" /> + <output filename="grit/extensions_resources_map.h" + type="resource_map_header" /> + <output filename="extensions_resources.pak" type="data_package" /> + </outputs> + <release seq="1"> + <structures> + <structure name="IDR_MD_EXTENSIONS_EXTENSIONS_HTML" + file="extensions.html" + flattenhtml="true" + allowexternalscript="true" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_EXTENSIONS_JS" + file="extensions.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_CODE_SECTION_HTML" + file="code_section.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_CODE_SECTION_JS" + file="code_section.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_DETAIL_VIEW_HTML" + file="detail_view.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_DETAIL_VIEW_JS" + file="detail_view.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_DRAG_AND_DROP_HANDLER_HTML" + file="drag_and_drop_handler.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_DRAG_AND_DROP_HANDLER_JS" + file="drag_and_drop_handler.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_DROP_OVERLAY_HTML" + file="drop_overlay.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_DROP_OVERLAY_JS" + file="drop_overlay.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_ERROR_PAGE_HTML" + file="error_page.html" + flattenhtml="true" + allowexternalscript="true" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_ERROR_PAGE_JS" + file="error_page.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_KEYBOARD_SHORTCUTS_HTML" + file="keyboard_shortcuts.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_KEYBOARD_SHORTCUTS_JS" + file="keyboard_shortcuts.js" + type="chrome_html" /> + <if expr="chromeos"> + <structure name="IDR_MD_EXTENSIONS_KIOSK_BROWSER_PROXY_HTML" + file="kiosk_browser_proxy.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_KIOSK_BROWSER_PROXY_JS" + file="kiosk_browser_proxy.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_KIOSK_DIALOG_HTML" + file="kiosk_dialog.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_KIOSK_DIALOG_JS" + file="kiosk_dialog.js" + type="chrome_html" /> + </if> + <structure name="IDR_MD_EXTENSIONS_MANAGER_HTML" + file="manager.html" + type="chrome_html" + flattenhtml="true" + allowexternalscript="true" /> + <structure name="IDR_MD_EXTENSIONS_MANAGER_JS" + file="manager.js" + type="chrome_html" + flattenhtml="true" /> + <structure name="IDR_MD_EXTENSIONS_ICONS_HTML" + file="icons.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_ITEM_HTML" + file="item.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_ITEM_JS" + file="item.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_ITEM_LIST_HTML" + file="item_list.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_ITEM_LIST_JS" + file="item_list.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_ITEM_UTIL_HTML" + file="item_util.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_ITEM_UTIL_JS" + file="item_util.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_LOAD_ERROR_HTML" + file="load_error.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_LOAD_ERROR_JS" + file="load_error.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_NAVIGATION_HELPER_HTML" + file="navigation_helper.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_NAVIGATION_HELPER_JS" + file="navigation_helper.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_OPTIONS_DIALOG_HTML" + file="options_dialog.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_OPTIONS_DIALOG_JS" + file="options_dialog.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_PACK_DIALOG_HTML" + file="pack_dialog.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_PACK_DIALOG_JS" + file="pack_dialog.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_PACK_DIALOG_ALERT_HTML" + file="pack_dialog_alert.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_PACK_DIALOG_ALERT_JS" + file="pack_dialog_alert.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_SERVICE_HTML" + file="service.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_SERVICE_JS" + file="service.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_SHORTCUT_INPUT_HTML" + file="shortcut_input.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_SHORTCUT_INPUT_JS" + file="shortcut_input.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_SHORTCUT_UTIL_HTML" + file="shortcut_util.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_SHORTCUT_UTIL_JS" + file="shortcut_util.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_SIDEBAR_HTML" + file="sidebar.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_SIDEBAR_JS" + file="sidebar.js" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_TOOLBAR_HTML" + file="toolbar.html" + type="chrome_html" + flattenhtml="true" + allowexternalscript="true" /> + <structure name="IDR_MD_EXTENSIONS_TOOLBAR_JS" + file="toolbar.js" + type="chrome_html" + flattenhtml="true" /> + <structure name="IDR_MD_EXTENSIONS_STRINGS_HTML" + file="strings.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_VIEW_MANAGER_HTML" + file="view_manager.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_VIEW_MANAGER_JS" + file="view_manager.js" + type="chrome_html" /> + </structures> + </release> +</grit> diff --git a/chromium/chrome/browser/resources/md_extensions/extensions_resources_vulcanized.grd b/chromium/chrome/browser/resources/md_extensions/extensions_resources_vulcanized.grd new file mode 100644 index 00000000000..d3cb02311e7 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/extensions_resources_vulcanized.grd @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> + <outputs> + <output filename="grit/extensions_resources.h" type="rc_header"> + <emit emit_type='prepend'></emit> + </output> + <output filename="grit/extensions_resources_map.cc" + type="resource_file_map_source" /> + <output filename="grit/extensions_resources_map.h" + type="resource_map_header" /> + <output filename="extensions_resources.pak" type="data_package" /> + </outputs> + <release seq="1"> + <includes> + <include name="IDR_MD_EXTENSIONS_VULCANIZED_HTML" file="${root_gen_dir}\chrome\browser\resources\md_extensions\vulcanized.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> + <include name="IDR_MD_EXTENSIONS_CRISPER_JS" file="${root_gen_dir}\chrome\browser\resources\md_extensions\crisper.js" use_base_dir="false" flattenhtml="true" type="BINDATA" compress="gzip" /> + </includes> + </release> +</grit> diff --git a/chromium/chrome/browser/resources/md_extensions/item.html b/chromium/chrome/browser/resources/md_extensions/item.html index c396d2e4c95..2f35703d131 100644 --- a/chromium/chrome/browser/resources/md_extensions/item.html +++ b/chromium/chrome/browser/resources/md_extensions/item.html @@ -1,29 +1,28 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> -<link rel="import" href="chrome://resources/cr_elements/paper_toggle_style_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.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> -<link rel="import" href="chrome://resources/html/load_time_data.html"> -<link rel="import" href="chrome://extensions/icons.html"> -<link rel="import" href="chrome://extensions/item_util.html"> -<link rel="import" href="chrome://extensions/strings.html"> +<link rel="import" href="icons.html"> +<link rel="import" href="item_util.html"> +<link rel="import" href="strings.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/communication-icons.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="chrome://resources/polymer/v1_0/paper-styles/shadow.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> -<link rel="import" href="chrome://extensions/navigation_helper.html"> +<link rel="import" href="navigation_helper.html"> <dom-module id="extensions-item"> <template> - <style include="iron-flex cr-hidden-style cr-icons action-link paper-button-style paper-toggle-style"> + <style include= + "iron-flex cr-hidden-style cr-icons action-link paper-button-style"> #icon-wrapper { align-self: flex-start; display: flex; @@ -41,21 +40,18 @@ display: flex; flex-direction: column; height: 160px; - width: 400px; + width: var(--extensions-card-width, 400px); } #card.dev-mode { height: 208px; } - #card.disabled { - opacity: 0.6; - } - #main { display: flex; flex-grow: 1; padding: 16px 20px 17px; + position: relative; } #content { @@ -135,26 +131,33 @@ color: white; display: none; margin-top: 8px; + max-width: 150px; opacity: 0.6; padding: 8px 12px; - position: absolute; transform: translateX(-50%); /* Move back 50% of width so that the text and icon share an x-center. */ } + :host-context([dir='rtl']) #source-indicator-text { + transform: translateX(50%); + } + #source-indicator:hover #source-indicator-text { display: block; } - paper-toggle-button { + cr-toggle { -webkit-margin-end: 8px; /* Avoid ripple from overlapping container. */ - cursor: pointer; } .action-button { color: var(--google-blue-500); } + #errors-button { + color: var(--google-red-700); + } + #dev-reload-button { -webkit-margin-end: 12px; } @@ -255,13 +258,13 @@ hidden$="[[!isTerminated_(data.state)]]"> $i18n{itemReload} </paper-button> - <paper-toggle-button id="enable-toggle" class="action-button" + <cr-toggle id="enable-toggle" class="action-button" checked="[[isEnabled_(data.state)]]" on-change="onEnableChange_" disabled="[[!isEnableToggleEnabled_(data.*)]]" hidden$="[[!showEnableToggle_(data.*)]]"> - </paper-toggle-button> + </cr-toggle> </div> </div> </template> - <script src="chrome://extensions/item.js"></script> + <script src="item.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/item.js b/chromium/chrome/browser/resources/md_extensions/item.js index eb7c4c4ebbf..f76a9fc0c95 100644 --- a/chromium/chrome/browser/resources/md_extensions/item.js +++ b/chromium/chrome/browser/resources/md_extensions/item.js @@ -4,57 +4,55 @@ cr.define('extensions', function() { /** @interface */ - const ItemDelegate = function() {}; - - ItemDelegate.prototype = { + class ItemDelegate { /** @param {string} id */ - deleteItem: assertNotReached, + deleteItem(id) {} /** * @param {string} id * @param {boolean} isEnabled */ - setItemEnabled: assertNotReached, + setItemEnabled(id, isEnabled) {} /** * @param {string} id * @param {boolean} isAllowedIncognito */ - setItemAllowedIncognito: assertNotReached, + setItemAllowedIncognito(id, isAllowedIncognito) {} /** * @param {string} id * @param {boolean} isAllowedOnFileUrls */ - setItemAllowedOnFileUrls: assertNotReached, + setItemAllowedOnFileUrls(id, isAllowedOnFileUrls) {} /** * @param {string} id * @param {boolean} isAllowedOnAllSites */ - setItemAllowedOnAllSites: assertNotReached, + setItemAllowedOnAllSites(id, isAllowedOnAllSites) {} /** * @param {string} id * @param {boolean} collectsErrors */ - setItemCollectsErrors: assertNotReached, + setItemCollectsErrors(id, collectsErrors) {} /** * @param {string} id * @param {chrome.developerPrivate.ExtensionView} view */ - inspectItemView: assertNotReached, + inspectItemView(id, view) {} /** @param {string} id */ - reloadItem: assertNotReached, + reloadItem(id) {} /** @param {string} id */ - repairItem: assertNotReached, + repairItem(id) {} /** @param {string} id */ - showItemOptionsPage: assertNotReached, - }; + showItemOptionsPage(id) {} + } const Item = Polymer({ is: 'extensions-item', @@ -100,7 +98,6 @@ cr.define('extensions', function() { assert(this.data); idElement.innerHTML = this.i18n('itemId', this.data.id); } - this.fire('extension-item-size-changed', {item: this.data}); }, /** @@ -282,11 +279,11 @@ cr.define('extensions', function() { */ computeDevReloadButtonHidden_: function() { // Only display the reload spinner if the extension is unpacked and - // not terminated (since if it's terminated, we'll show a crashed reload - // buton). + // enabled. There's no point in reloading a disabled extension, and we'll + // show a crashed reload buton if it's terminated. const showIcon = this.data.location == chrome.developerPrivate.Location.UNPACKED && - this.data.state != chrome.developerPrivate.ExtensionState.TERMINATED; + this.data.state == chrome.developerPrivate.ExtensionState.ENABLED; return !showIcon; }, diff --git a/chromium/chrome/browser/resources/md_extensions/item_list.html b/chromium/chrome/browser/resources/md_extensions/item_list.html index 78791afb6da..13b0c0bc166 100644 --- a/chromium/chrome/browser/resources/md_extensions/item_list.html +++ b/chromium/chrome/browser/resources/md_extensions/item_list.html @@ -1,21 +1,21 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-resizable-behavior/iron-resizable-behavior.html"> -<link rel="import" href="chrome://extensions/item.html"> +<link rel="import" href="item.html"> <dom-module id="extensions-item-list"> <template> - <style include="cr-hidden-style"> - :host { - @apply(--layout-vertical); + <style include="cr-shared-style"> + #items-container, + extensions-item { + --extensions-card-width: 400px; } - iron-list { - @apply(--layout-flex); - margin-top: 18px; + #container { + height: 100%; + overflow: overlay; } .empty-list-message { @@ -26,29 +26,45 @@ text-align: center; } - .wrapper { - padding: 6px; + #items-container { + --grid-gutter: 12px; + --max-columns: 3; + display: grid; + grid-column-gap: var(--grid-gutter); + grid-row-gap: var(--grid-gutter); + grid-template-columns: repeat(auto-fill, var(--extensions-card-width)); + justify-content: center; + margin: auto; + max-width: calc(var(--extensions-card-width) * var(--max-columns) + + var(--grid-gutter) * var(--max-columns)); + padding: calc(var(--grid-gutter) / 2); } - </style> - <div id="no-items" class="empty-list-message" - hidden$="[[!shouldShowEmptyItemsMessage_(items.length)]]"> - <span>$i18nRaw{noExtensionsOrApps}</span> - </div> - <div id="no-search-results" class="empty-list-message" - hidden$="[[!shouldShowEmptySearchMessage_(shownItems_.length)]]"> - <span>$i18n{noSearchResults}</span> - </div> - <iron-list id="list" items="[[shownItems_]]" - as="item" grid> - <template> - <div class="wrapper"> + extensions-item { + grid-column-start: auto; + grid-row-start: auto; + } + </style> + <div id="container"> + <div class="empty-list-message" hidden="[[!isGuest]]"> + $i18n{guestModeMessage} + </div> + <div id="no-items" class="empty-list-message" + hidden$="[[!shouldShowEmptyItemsMessage_(items.length)]]"> + <span>$i18nRaw{noExtensionsOrApps}</span> + </div> + <div id="no-search-results" class="empty-list-message" + hidden$="[[!shouldShowEmptySearchMessage_(shownItems_.length)]]"> + <span>$i18n{noSearchResults}</span> + </div> + <div id="items-container"> + <template is="dom-repeat" items="[[shownItems_]]"> <extensions-item data="[[item]]" delegate="[[delegate]]" - id="[[item.id]]" in-dev-mode="[[inDevMode]]"> + in-dev-mode="[[inDevMode]]"> </extensions-item> - </div> - </template> - </iron-list> + </template> + </div> + </div> </template> - <script src="chrome://extensions/item_list.js"></script> + <script src="item_list.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/item_list.js b/chromium/chrome/browser/resources/md_extensions/item_list.js index a2a38e15a04..e6cd997e5ea 100644 --- a/chromium/chrome/browser/resources/md_extensions/item_list.js +++ b/chromium/chrome/browser/resources/md_extensions/item_list.js @@ -6,7 +6,7 @@ cr.define('extensions', function() { const ItemList = Polymer({ is: 'extensions-item-list', - behaviors: [Polymer.IronResizableBehavior], + behaviors: [CrContainerShadowBehavior], properties: { /** @type {Array<!chrome.developerPrivate.ExtensionInfo>} */ @@ -20,6 +20,8 @@ cr.define('extensions', function() { value: false, }, + isGuest: Boolean, + filter: String, /** @private {Array<!chrome.developerPrivate.ExtensionInfo>} */ @@ -29,22 +31,6 @@ cr.define('extensions', function() { } }, - listeners: { - 'list.extension-item-size-changed': 'itemSizeChanged_', - 'view-enter-start': 'onViewEnterStart_', - }, - - /** - * Updates the size for a given item. - * @param {CustomEvent} e - * @private - * @suppress {checkTypes} Closure doesn't know $.list is an IronList. - */ - itemSizeChanged_: function(e) { - this.$.list.updateSizeForItem(e.detail.item); - this.fire('resize'); - }, - /** * Computes the list of items to be shown. * @param {Object} changeRecord The changeRecord for |items|. @@ -53,24 +39,21 @@ cr.define('extensions', function() { * @private */ computeShownItems_: function(changeRecord, filter) { - return this.items.filter(function(item) { - return item.name.toLowerCase().includes(this.filter.toLowerCase()); - }, this); + const formattedFilter = this.filter.trim().toLowerCase(); + return this.items.filter( + item => item.name.toLowerCase().includes(formattedFilter)); }, + /** @private */ shouldShowEmptyItemsMessage_: function() { - return this.items.length === 0; + return !this.isGuest && this.items.length === 0; }, + /** @private */ shouldShowEmptySearchMessage_: function() { - return !this.shouldShowEmptyItemsMessage_() && + return !this.isGuest && !this.shouldShowEmptyItemsMessage_() && this.shownItems_.length === 0; }, - - /** @private */ - onViewEnterStart_: function() { - this.fire('resize'); // This is needed to correctly render iron-list. - }, }); return { diff --git a/chromium/chrome/browser/resources/md_extensions/item_util.html b/chromium/chrome/browser/resources/md_extensions/item_util.html index 6429c3f459a..8836ce076d2 100644 --- a/chromium/chrome/browser/resources/md_extensions/item_util.html +++ b/chromium/chrome/browser/resources/md_extensions/item_util.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/html/load_time_data.html"> -<script src="chrome://extensions/item_util.js"></script> +<link rel="import" href="strings.html"> +<script src="item_util.js"></script> diff --git a/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html b/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html index 1678a875485..ecde827f4f7 100644 --- a/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html +++ b/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html @@ -1,19 +1,31 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/md_select_css.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/shadow.html"> -<link rel="import" href="chrome://extensions/shortcut_input.html"> +<link rel="import" href="shortcut_input.html"> <dom-module id="extensions-keyboard-shortcuts"> <template> - <style include="md-select cr-hidden-style"> + <style include="md-select cr-shared-style"> + :host { + height: 100%; + } + + #container { + height: 100%; + overflow: overlay; + padding-top: 24px; + } + .shortcut-card { + @apply(--cr-primary-text); @apply(--shadow-elevation-2dp); background-color: white; - color: var(--paper-grey-600); margin: 0 auto 16px auto; max-width: 928px; min-width: 600px; @@ -24,26 +36,35 @@ .command-entry { align-items: center; display: flex; - height: 48px; + height: var(--cr-section-min-height); } .command-name { - color: var(--paper-grey-900); flex: 1; } .command-entry .md-select-wrapper { - -webkit-margin-start: 40px; + -webkit-margin-start: var(--cr-section-padding); + } + + /* Vertically align with paper-input. */ + .command-entry .md-select { + /* TODO(scottchen): line-height needs adjustment to fix large fonts, + * same for paper-input. */ + line-height: 22px; + /* Below rules help align md-select and paper-input underline. */ + margin-top: 3px; + padding-bottom: 0; + padding-top: 0; } .card-title { align-items: center; - border-bottom: 1px solid var(--paper-grey-400); + border-bottom: var(--cr-separator-line); display: flex; - font-size: 14px; - font-weight: 500; margin-bottom: 9px; - padding: 16px 20px; + padding: 16px var(--cr-section-padding); + @apply(--cr-title-text); } .icon { @@ -58,12 +79,8 @@ -webkit-margin-end: 20px; -webkit-margin-start: 56px; } - - #main { - margin-top: 24px; - } </style> - <div id="main"> + <div id="container"> <template is="dom-repeat" items="[[calculateShownItems_(items.*)]]"> <div class="shortcut-card"> <div class="card-title"> @@ -101,5 +118,5 @@ </template> </div> </template> - <script src="chrome://extensions/keyboard_shortcuts.js"></script> + <script src="keyboard_shortcuts.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.js b/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.js index 160e3ce1097..e75b5cecdab 100644 --- a/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.js +++ b/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.js @@ -9,6 +9,8 @@ cr.define('extensions', function() { const KeyboardShortcuts = Polymer({ is: 'extensions-keyboard-shortcuts', + behaviors: [CrContainerShadowBehavior], + properties: { /** @type {Array<!chrome.developerPrivate.ExtensionInfo>} */ items: Array, diff --git a/chromium/chrome/browser/resources/md_extensions/kiosk_browser_proxy.html b/chromium/chrome/browser/resources/md_extensions/kiosk_browser_proxy.html new file mode 100644 index 00000000000..58f9bb8fe27 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/kiosk_browser_proxy.html @@ -0,0 +1,2 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="kiosk_browser_proxy.js"></script> diff --git a/chromium/chrome/browser/resources/md_extensions/kiosk_browser_proxy.js b/chromium/chrome/browser/resources/md_extensions/kiosk_browser_proxy.js new file mode 100644 index 00000000000..91e7a70d4ca --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/kiosk_browser_proxy.js @@ -0,0 +1,108 @@ +// 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. + +/** + * @fileoverview A helper object used from the "Kiosk" dialog to interact with + * the browser. + */ + +/** + * @typedef {{ + * kioskEnabled: boolean, + * autoLaunchEnabled: boolean + * }} + */ +let KioskSettings; + +/** + * @typedef {{ + * id: string, + * name: string, + * iconURL: string, + * autoLaunch: boolean, + * isLoading: boolean + * }} + */ +let KioskApp; + +/** + * @typedef {{ + * apps: !Array<!KioskApp>, + * disableBailout: boolean, + * hasAutoLaunchApp: boolean + * }} + */ +let KioskAppSettings; + +cr.define('extensions', function() { + + /** @interface */ + class KioskBrowserProxy { + /** @param {string} appId */ + addKioskApp(appId) {} + + /** @param {string} appId */ + disableKioskAutoLaunch(appId) {} + + /** @param {string} appId */ + enableKioskAutoLaunch(appId) {} + + /** @return {!Promise<!KioskAppSettings>} */ + getKioskAppSettings() {} + + /** @return {!Promise<!KioskSettings>} */ + initializeKioskAppSettings() {} + + /** @param {string} appId */ + removeKioskApp(appId) {} + + /** @param {boolean} disableBailout */ + setDisableBailoutShortcut(disableBailout) {} + } + + /** @implements {extensions.KioskBrowserProxy} */ + class KioskBrowserProxyImpl { + /** @override */ + initializeKioskAppSettings() { + return cr.sendWithPromise('initializeKioskAppSettings'); + } + + /** @override */ + getKioskAppSettings() { + return cr.sendWithPromise('getKioskAppSettings'); + } + + /** @override */ + addKioskApp(appId) { + chrome.send('addKioskApp', [appId]); + } + + /** @override */ + disableKioskAutoLaunch(appId) { + chrome.send('disableKioskAutoLaunch', [appId]); + } + + /** @override */ + enableKioskAutoLaunch(appId) { + chrome.send('enableKioskAutoLaunch', [appId]); + } + + /** @override */ + removeKioskApp(appId) { + chrome.send('removeKioskApp', [appId]); + } + + /** @override */ + setDisableBailoutShortcut(disableBailout) { + chrome.send('setDisableBailoutShortcut', [disableBailout]); + } + } + + cr.addSingletonGetter(KioskBrowserProxyImpl); + + return { + KioskBrowserProxy: KioskBrowserProxy, + KioskBrowserProxyImpl: KioskBrowserProxyImpl, + }; +}); diff --git a/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html b/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html new file mode 100644 index 00000000000..51100a4ec56 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html @@ -0,0 +1,144 @@ +<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_icons_css.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> +<link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/cr.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/paper-button/paper-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.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/paper-input/paper-input.html"> +<link rel="import" href="kiosk_browser_proxy.html"> + +<dom-module id="extensions-kiosk-dialog"> + <template> + <style include="cr-shared-style paper-button-style cr-icons"> + #add-kiosk-app { + --paper-input-container-input: { + font-size: inherit; + }; + align-items: center; + display: flex; + margin-bottom: 10px; + margin-top: 20px; + width: 350px; + } + + #add-kiosk-app paper-input { + flex: 1; + } + + #add-kiosk-app paper-button { + -webkit-margin-start: 10px; + } + + paper-button { + color: var(--google-blue-500); + } + + #kiosk-apps-list { + border: 1px solid var(--paper-grey-300); + padding: 10px; + } + + .list-item { + align-items: center; + border-bottom: 1px solid var(--paper-grey-300); + display: flex; + justify-content: space-between; + padding: 5px; + } + + .list-item:last-of-type { + border-bottom: none; + } + + .list-item:hover { + background-color: var(--paper-grey-300); + } + + .item-icon { + vertical-align: middle; + width: 25px; + } + + .item-controls { + visibility: hidden; + } + + .list-item:hover .item-controls { + visibility: visible; + } + </style> + <dialog is="cr-dialog" id="dialog" close-text="$i18n{close}" + ignore-enter-key> + <div slot="title">$i18n{manageKioskApp}</div> + <div slot="body"> + <div id="kiosk-apps-list"> + <template is="dom-repeat" items="[[apps_]]"> + <div class="list-item"> + <div class="item-name"> + <img class="item-icon" src="[[item.iconURL]]"> + [[item.name]] + <span hidden="[[!item.autoLaunch]]"> + $i18n{kioskAutoLaunch} + </span> + </div> + <div class="item-controls"> + <paper-button hidden="[[!canEditAutoLaunch_]]" + on-tap="onAutoLaunchButtonTap_"> + [[getAutoLaunchButtonLabel_(item.autoLaunch, + '$i18nPolymer{kioskDisableAutoLaunch}', + '$i18nPolymer{kioskEnableAutoLaunch}')]] + </paper-button> + <button is="paper-icon-button-light" class="icon-delete-gray" + on-tap="onDeleteAppTap_"></button> + </div> + </div> + </template> + </div> + <div id="add-kiosk-app"> + <paper-input id="add-input" label="$i18n{kioskAddApp}" + placeholder="$i18n{kioskAddAppHint}" value="{{addAppInput_}}" + always-float-label invalid="[[errorAppId_]]" + error-message="[[getErrorMessage_( + '$i18nPolymer{kioskInvalidApp}', errorAppId_)]]" + on-keydown="clearInputInvalid_"> + </paper-input> + <paper-button id="add-button" on-tap="onAddAppTap_" + disabled="[[!addAppInput_]]"> + $i18n{add} + </paper-button> + </div> + <paper-checkbox disabled="[[!canEditBailout_]]" + on-pointerdown="onBailoutTap_" checked="[[bailoutDisabled_]]" + hidden="[[!canEditAutoLaunch_]]"> + $i18n{kioskDisableBailout} + </paper-checkbox> + </div> + <div slot="button-container"> + <paper-button class="action-button" on-tap="onDoneTap_"> + $i18n{done} + </paper-button> + </div> + </dialog> + <dialog is="cr-dialog" id="confirm-dialog" close-text="$i18n{close}" + ignore-enter-key on-close="stopPropagation_"> + <div slot="title">$i18n{kioskDisableBailoutWarningTitle}</div> + <div slot="body">$i18n{kioskDisableBailoutWarningBody}</div> + <div slot="button-container"> + <paper-button class="cancel-button" on-tap="onBailoutDialogCancelTap_"> + $i18n{cancel} + </paper-button> + <paper-button class="action-button" on-tap="onBailoutDialogConfirmTap_"> + $i18n{confirm} + </paper-button> + </div> + </dialog> + </template> + <script src="kiosk_dialog.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.js b/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.js new file mode 100644 index 00000000000..254c2c14d81 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.js @@ -0,0 +1,185 @@ +// 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. + +cr.define('extensions', function() { + 'use strict'; + + const KioskDialog = Polymer({ + is: 'extensions-kiosk-dialog', + behaviors: [WebUIListenerBehavior], + properties: { + /** @private {?string} */ + addAppInput_: { + type: String, + value: null, + }, + + /** @private {!Array<!KioskApp>} */ + apps_: Array, + + /** @private */ + bailoutDisabled_: Boolean, + + /** @private */ + canEditAutoLaunch_: Boolean, + + /** @private */ + canEditBailout_: Boolean, + + /** @private {?string} */ + errorAppId_: String, + }, + + /** @private {?extensions.KioskBrowserProxy} */ + kioskBrowserProxy_: null, + + /** @override */ + ready: function() { + this.kioskBrowserProxy_ = extensions.KioskBrowserProxyImpl.getInstance(); + }, + + /** @override */ + attached: function() { + this.kioskBrowserProxy_.initializeKioskAppSettings() + .then(params => { + this.canEditAutoLaunch_ = params.autoLaunchEnabled; + return this.kioskBrowserProxy_.getKioskAppSettings(); + }) + .then(this.setSettings_.bind(this)); + + this.addWebUIListener( + 'kiosk-app-settings-changed', this.setSettings_.bind(this)); + this.addWebUIListener('kiosk-app-updated', this.updateApp_.bind(this)); + this.addWebUIListener('kiosk-app-error', this.showError_.bind(this)); + + this.$.dialog.showModal(); + }, + + /** + * @param {!KioskAppSettings} settings + * @private + */ + setSettings_: function(settings) { + this.apps_ = settings.apps; + this.bailoutDisabled_ = settings.disableBailout; + this.canEditBailout_ = settings.hasAutoLaunchApp; + }, + + /** + * @param {!KioskApp} app + * @private + */ + updateApp_: function(app) { + const index = this.apps_.findIndex(a => a.id == app.id); + assert(index < this.apps_.length); + this.set('apps_.' + index, app); + }, + + /** + * @param {string} appId + * @private + */ + showError_: function(appId) { + this.errorAppId_ = appId; + }, + + /** + * @param {string} errorMessage + * @return {string} + * @private + */ + getErrorMessage_: function(errorMessage) { + return this.errorAppId_ + ' ' + errorMessage; + }, + + /** @private */ + onAddAppTap_: function() { + assert(this.addAppInput_); + this.kioskBrowserProxy_.addKioskApp(this.addAppInput_); + this.addAppInput_ = null; + }, + + /** @private */ + clearInputInvalid_: function() { + this.errorAppId_ = null; + }, + + /** + * @param {{model: {item: !KioskApp}}} event + * @private + */ + onAutoLaunchButtonTap_: function(event) { + const app = event.model.item; + if (app.autoLaunch) { // If the app is originally set to + // auto-launch. + this.kioskBrowserProxy_.disableKioskAutoLaunch(app.id); + } else { + this.kioskBrowserProxy_.enableKioskAutoLaunch(app.id); + } + }, + + /** + * @param {!Event} event + * @private + */ + onBailoutTap_: function(event) { + event.preventDefault(); + if (this.bailoutDisabled_) { + this.kioskBrowserProxy_.setDisableBailoutShortcut(false); + this.bailoutDisabled_ = false; + this.$['confirm-dialog'].close(); + } else { + this.$['confirm-dialog'].showModal(); + } + }, + + /** @private */ + onBailoutDialogCancelTap_: function() { + this.$['confirm-dialog'].cancel(); + }, + + /** @private */ + onBailoutDialogConfirmTap_: function() { + this.kioskBrowserProxy_.setDisableBailoutShortcut(true); + this.bailoutDisabled_ = true; + this.$['confirm-dialog'].close(); + }, + + /** @private */ + onDoneTap_: function() { + this.$.dialog.close(); + }, + + /** + * @param {{model: {item: !KioskApp}}} event + * @private + */ + onDeleteAppTap_: function(event) { + this.kioskBrowserProxy_.removeKioskApp(event.model.item.id); + }, + + /** + * @param {boolean} autoLaunched + * @param {string} disableStr + * @param {string} enableStr + * @return {string} + * @private + */ + getAutoLaunchButtonLabel_: function(autoLaunched, disableStr, enableStr) { + return autoLaunched ? disableStr : enableStr; + }, + + /** + * @param {!Event} e + * @private + */ + stopPropagation_: function(e) { + e.stopPropagation(); + }, + }); + + return { + KioskDialog: KioskDialog, + }; +}); diff --git a/chromium/chrome/browser/resources/md_extensions/load_error.html b/chromium/chrome/browser/resources/md_extensions/load_error.html index 1f3212d870a..0d0f1237a5c 100644 --- a/chromium/chrome/browser/resources/md_extensions/load_error.html +++ b/chromium/chrome/browser/resources/md_extensions/load_error.html @@ -6,7 +6,7 @@ <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/paper-button/paper-button.html"> -<link rel="import" href="chrome://extensions/code_section.html"> +<link rel="import" href="code_section.html"> <dom-module id="extensions-load-error"> <template> @@ -47,5 +47,5 @@ </div> </dialog> </template> - <script src="chrome://extensions/load_error.js"></script> + <script src="load_error.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/load_error.js b/chromium/chrome/browser/resources/md_extensions/load_error.js index 5d1cc8e98dc..67391a189ff 100644 --- a/chromium/chrome/browser/resources/md_extensions/load_error.js +++ b/chromium/chrome/browser/resources/md_extensions/load_error.js @@ -6,15 +6,13 @@ cr.define('extensions', function() { 'use strict'; /** @interface */ - function LoadErrorDelegate() {} - - LoadErrorDelegate.prototype = { + class LoadErrorDelegate { /** * Attempts to load the previously-attempted unpacked extension. * @param {string} retryId */ - retryLoadUnpacked: assertNotReached, - }; + retryLoadUnpacked(retryId) {} + } const LoadError = Polymer({ is: 'extensions-load-error', diff --git a/chromium/chrome/browser/resources/md_extensions/manager.html b/chromium/chrome/browser/resources/md_extensions/manager.html index 6b401d1dafc..c38263894b1 100644 --- a/chromium/chrome/browser/resources/md_extensions/manager.html +++ b/chromium/chrome/browser/resources/md_extensions/manager.html @@ -7,44 +7,43 @@ <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/html/promise_resolver.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-header-panel/paper-header-panel.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> -<link rel="import" href="chrome://extensions/detail_view.html"> -<link rel="import" href="chrome://extensions/drop_overlay.html"> -<link rel="import" href="chrome://extensions/error_page.html"> -<link rel="import" href="chrome://extensions/item_list.html"> -<link rel="import" href="chrome://extensions/item_util.html"> -<link rel="import" href="chrome://extensions/keyboard_shortcuts.html"> -<link rel="import" href="chrome://extensions/load_error.html"> -<link rel="import" href="chrome://extensions/navigation_helper.html"> -<link rel="import" href="chrome://extensions/options_dialog.html"> -<link rel="import" href="chrome://extensions/pack_dialog.html"> -<link rel="import" href="chrome://extensions/service.html"> -<link rel="import" href="chrome://extensions/sidebar.html"> -<link rel="import" href="chrome://extensions/toolbar.html"> -<link rel="import" href="chrome://extensions/view_manager.html"> +<link rel="import" href="detail_view.html"> +<link rel="import" href="drop_overlay.html"> +<link rel="import" href="error_page.html"> +<link rel="import" href="item_list.html"> +<link rel="import" href="item_util.html"> +<link rel="import" href="keyboard_shortcuts.html"> +<link rel="import" href="load_error.html"> +<link rel="import" href="navigation_helper.html"> +<link rel="import" href="options_dialog.html"> +<link rel="import" href="pack_dialog.html"> +<link rel="import" href="service.html"> +<link rel="import" href="sidebar.html"> +<link rel="import" href="toolbar.html"> +<link rel="import" href="view_manager.html"> <link rel="stylesheet" href="chrome://resources/css/md_colors.css"> +<if expr="chromeos"> +<link rel="import" href="kiosk_browser_proxy.html"> +<link rel="import" href="kiosk_dialog.html"> +</if> + <dom-module id="extensions-manager"> <template> <style include="cr-hidden-style"> :host { + display: flex; + flex-direction: column; height: 100%; } - #panel { - --paper-header-panel-standard-container: { - display: flex; - }; - } - extensions-sidebar { flex-basis: 256px; } #viewManager { - flex-grow: 1; - overflow-y: auto; + flex: 1; + position: relative; } extensions-item { @@ -56,41 +55,52 @@ } </style> <extensions-drop-overlay></extensions-drop-overlay> - <paper-header-panel id="panel"> - <extensions-toolbar class="paper-header" in-dev-mode="[[inDevMode]]" - on-pack-tap="onPackTap_" delegate="[[delegate]]" - on-cr-toolbar-menu-tap="onMenuButtonTap_" - on-search-changed="onFilterChanged_"> - </extensions-toolbar> - <dialog id="drawer" is="cr-drawer" heading="$i18n{toolbarTitle}"> - <div class="drawer-content"> - <extensions-sidebar id="sidebar"></extensions-sidebar> - </div> - </dialog> - <extensions-view-manager id="viewManager"> - <extensions-item-list id="items-list" - items="[[itemsList_]]" - delegate="[[delegate]]" in-dev-mode="[[inDevMode]]" - filter="[[filter]]" hidden$="[[!didInitPage_]]" slot="view"> - </extensions-item-list> - <extensions-detail-view id="details-view" delegate="[[delegate]]" - in-dev-mode="[[inDevMode]]" data="[[detailViewItem_]]" - slot="view"> - </extensions-detail-view> - <extensions-keyboard-shortcuts id="keyboard-shortcuts" - items="[[extensions]]" slot="view"> - </extensions-keyboard-shortcuts> - <extensions-error-page id="error-page" - data="[[errorPageItem_]]" delegate="[[delegate]]" slot="view"> - </extensions-error-page> - </extensions-view-manager> - <extensions-options-dialog id="options-dialog"> - </extensions-options-dialog> - <extensions-pack-dialog id="pack-dialog" delegate="[[delegate]]"> - </extensions-pack-dialog> - <extensions-load-error id="load-error" delegate="[[delegate]]"> - </extensions-load-error> - </paper-header-panel> + <extensions-toolbar is-guest="[[isGuest_]]" in-dev-mode="[[inDevMode]]" + on-pack-tap="onPackTap_" delegate="[[delegate]]" + on-cr-toolbar-menu-tap="onMenuButtonTap_" + on-search-changed="onFilterChanged_" +<if expr="chromeos"> + on-kiosk-tap="onKioskTap_" + kiosk-enabled="[[kioskEnabled_]]" +</if> + > + </extensions-toolbar> + <dialog id="drawer" is="cr-drawer" heading="$i18n{toolbarTitle}" + align="$i18n{textdirection}"> + <div class="drawer-content"> + <extensions-sidebar id="sidebar"></extensions-sidebar> + </div> + </dialog> + <extensions-view-manager id="viewManager" role="main"> + <extensions-item-list id="items-list" items="[[itemsList_]]" + delegate="[[delegate]]" in-dev-mode="[[inDevMode]]" + filter="[[filter]]" hidden$="[[!didInitPage_]]" slot="view" + is-guest="[[isGuest_]]"> + </extensions-item-list> + <extensions-detail-view id="details-view" delegate="[[delegate]]" + in-dev-mode="[[inDevMode]]" data="[[detailViewItem_]]" + slot="view"> + </extensions-detail-view> + <extensions-keyboard-shortcuts id="keyboard-shortcuts" + items="[[extensions]]" slot="view"> + </extensions-keyboard-shortcuts> + <extensions-error-page id="error-page" + data="[[errorPageItem_]]" delegate="[[delegate]]" slot="view"> + </extensions-error-page> + </extensions-view-manager> + <extensions-options-dialog id="options-dialog"> + </extensions-options-dialog> + <extensions-pack-dialog id="pack-dialog" delegate="[[delegate]]"> + </extensions-pack-dialog> + <extensions-load-error id="load-error" delegate="[[delegate]]"> + </extensions-load-error> +<if expr="chromeos"> + <template is="dom-if" if="[[showKioskDialog_]]" restamp> + <extensions-kiosk-dialog id="kiosk-dialog" + on-close="onKioskDialogClose_"> + </extensions-kiosk-dialog> + </template> +</if> </template> - <script src="chrome://extensions/manager.js"></script> -</dom-module>
\ No newline at end of file + <script src="manager.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/manager.js b/chromium/chrome/browser/resources/md_extensions/manager.js index 9ac31f6fd5a..eb197cabeb3 100644 --- a/chromium/chrome/browser/resources/md_extensions/manager.js +++ b/chromium/chrome/browser/resources/md_extensions/manager.js @@ -35,9 +35,6 @@ cr.define('extensions', function() { behaviors: [I18nBehavior], properties: { - /** @type {extensions.Sidebar} */ - sidebar: Object, - /** @type {extensions.Toolbar} */ toolbar: Object, @@ -45,6 +42,13 @@ cr.define('extensions', function() { // passed to different elements as different types. delegate: Object, + isGuest_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('isGuest'); + }, + }, + inDevMode: { type: Boolean, value: false, @@ -104,6 +108,20 @@ cr.define('extensions', function() { type: Boolean, value: false, }, + + // <if expr="chromeos"> + /** @private */ + kioskEnabled_: { + type: Boolean, + value: false, + }, + + /** @private */ + showKioskDialog_: { + type: Boolean, + value: false, + }, + // </if> }, /** @@ -113,19 +131,46 @@ cr.define('extensions', function() { */ currentPage_: null, + /** + * The ID of the listner on |extensions.navigation|. Stored so that the + * listener can be removed when this element is detached (happens in tests). + * @private {?number} + */ + navigationListener_: null, + + /** @override */ created: function() { this.readyPromiseResolver = new PromiseResolver(); }, + /** @override */ ready: function() { this.toolbar = /** @type {extensions.Toolbar} */ (this.$$('extensions-toolbar')); this.readyPromiseResolver.resolve(); - extensions.navigation.onRouteChanged(newPage => { + + // <if expr="chromeos"> + extensions.KioskBrowserProxyImpl.getInstance() + .initializeKioskAppSettings() + .then(params => { + this.kioskEnabled_ = params.kioskEnabled; + }); + // </if> + }, + + /** @override */ + attached: function() { + this.navigationListener_ = extensions.navigation.addListener(newPage => { this.changePage_(newPage); }); }, + /** @override */ + detached: function() { + assert(extensions.navigation.removeListener(this.navigationListener_)); + this.navigationListener_ = null; + }, + get keyboardShortcuts() { return this.$['keyboard-shortcuts']; }, @@ -165,7 +210,19 @@ cr.define('extensions', function() { /** @private */ onMenuButtonTap_: function() { - this.$.drawer.toggle(); + this.$.drawer.openDrawer(); + + // Sidebar needs manager to inform it of what to highlight since it + // has no access to item-specific page. + let page = extensions.navigation.getCurrentPage(); + if (page.extensionId) { + // Find out what type of item we're looking at, and replace page info + // with that list type. + const data = assert(this.getData_(page.extensionId)); + page = {page: Page.LIST, type: extensions.getItemListType(data)}; + } + + this.$.sidebar.updateSelected(page); }, /** @@ -288,7 +345,7 @@ cr.define('extensions', function() { */ changePage_: function(newPage) { this.$.drawer.closeDrawer(); - if (this.optionsDialog.open) + if (this.optionsDialog && this.optionsDialog.open) this.optionsDialog.close(); const fromPage = this.currentPage_ ? this.currentPage_.page : null; @@ -298,7 +355,7 @@ cr.define('extensions', function() { data = assert(this.getData_(newPage.extensionId)); if (newPage.hasOwnProperty('type')) - this.listType_ = newPage.type; + this.listType_ = /** @type {extensions.ShowingType} */ (newPage.type); if (toPage == Page.DETAILS) this.detailViewItem_ = assert(data); @@ -327,6 +384,17 @@ cr.define('extensions', function() { this.$['pack-dialog'].show(); }, + // <if expr="chromeos"> + /** @private */ + onKioskTap_: function() { + this.showKioskDialog_ = true; + }, + + onKioskDialogClose_: function() { + this.showKioskDialog_ = false; + }, + // </if> + /** * @param {!extensions.ShowingType} listType * @private diff --git a/chromium/chrome/browser/resources/md_extensions/navigation_helper.html b/chromium/chrome/browser/resources/md_extensions/navigation_helper.html index 15666da1184..2521c5d5f3f 100644 --- a/chromium/chrome/browser/resources/md_extensions/navigation_helper.html +++ b/chromium/chrome/browser/resources/md_extensions/navigation_helper.html @@ -1,3 +1,4 @@ <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<script src="chrome://extensions/navigation_helper.js"></script> +<link rel="import" href="strings.html"> +<script src="navigation_helper.js"></script> diff --git a/chromium/chrome/browser/resources/md_extensions/navigation_helper.js b/chromium/chrome/browser/resources/md_extensions/navigation_helper.js index 768b9f1aef0..9437730ea02 100644 --- a/chromium/chrome/browser/resources/md_extensions/navigation_helper.js +++ b/chromium/chrome/browser/resources/md_extensions/navigation_helper.js @@ -29,27 +29,52 @@ extensions.ShowingType = { /** @typedef {{page: Page, extensionId: (string|undefined), - subpage: (!Dialog|undefined)}} */ + subpage: (!Dialog|undefined), + type: (!extensions.ShowingType|undefined)}} */ let PageState; cr.define('extensions', function() { 'use strict'; /** + * Regular expression that captures the leading slash, the content and the + * trailing slash in three different groups. + * @const {!RegExp} + */ + const CANONICAL_PATH_REGEX = /(^\/)([\/-\w]+)(\/$)/; + + /** * A helper object to manage in-page navigations. Since the extensions page * needs to support different urls for different subpages (like the details * page), we use this object to manage the history and url conversions. */ class NavigationHelper { constructor() { - /** @private {!Array<function(!PageState)>} */ - this.listeners_ = []; + // Redirect if route not supported. + let validPathnames = ['/']; + if (!loadTimeData.getBoolean('isGuest')) { + validPathnames.push('/shortcuts', '/apps'); + } + if (!validPathnames.includes(this.currentPath_)) { + window.history.replaceState(undefined, '', '/'); + } + + /** @private {number} */ + this.nextListenerId_ = 1; + + /** @private {!Map<number, function(!PageState)>} */ + this.listeners_ = new Map(); window.addEventListener('popstate', () => { this.notifyRouteChanged_(this.getCurrentPage()); }); } + /** @private */ + get currentPath_() { + return location.pathname.replace(CANONICAL_PATH_REGEX, '$1$2'); + } + /** * @return {!PageState} The page that should be displayed for the current * URL. @@ -66,10 +91,10 @@ cr.define('extensions', function() { if (id) return {page: Page.ERRORS, extensionId: id}; - if (location.pathname == '/shortcuts') + if (this.currentPath_ == '/shortcuts') return {page: Page.SHORTCUTS}; - if (location.pathname == '/apps') + if (this.currentPath_ == '/apps') return {page: Page.LIST, type: extensions.ShowingType.APPS}; return {page: Page.LIST, type: extensions.ShowingType.EXTENSIONS}; @@ -78,9 +103,22 @@ cr.define('extensions', function() { /** * Function to add subscribers. * @param {!function(!PageState)} listener + * @return {number} A numerical ID to be used for removing the listener. */ - onRouteChanged(listener) { - this.listeners_.push(listener); + addListener(listener) { + const nextListenerId = this.nextListenerId_++; + this.listeners_.set(nextListenerId, listener); + return nextListenerId; + } + + /** + * Remove a previously registered listener. + * @param {number} id + * @return {boolean} Whether a listener with the given ID was actually found + * and removed. + */ + removeListener(id) { + return this.listeners_.delete(id); } /** @@ -88,9 +126,9 @@ cr.define('extensions', function() { * @private */ notifyRouteChanged_(newPage) { - for (const listener of this.listeners_) { + this.listeners_.forEach((listener, id) => { listener(newPage); - } + }); } /** @@ -117,7 +155,7 @@ cr.define('extensions', function() { let path; switch (entry.page) { case Page.LIST: - if (entry.type && entry.type == extensions.ShowingType.APPS) + if (entry.type == extensions.ShowingType.APPS) path = '/apps'; else path = '/'; @@ -157,6 +195,8 @@ cr.define('extensions', function() { const navigation = new NavigationHelper(); return { + // Constructor exposed for testing purposes. + NavigationHelper: NavigationHelper, navigation: navigation, }; }); diff --git a/chromium/chrome/browser/resources/md_extensions/options_dialog.html b/chromium/chrome/browser/resources/md_extensions/options_dialog.html index e7029bf8c76..8dd57e9fd69 100644 --- a/chromium/chrome/browser/resources/md_extensions/options_dialog.html +++ b/chromium/chrome/browser/resources/md_extensions/options_dialog.html @@ -3,7 +3,7 @@ <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<link rel="import" href="chrome://extensions/navigation_helper.html"> +<link rel="import" href="navigation_helper.html"> <dom-module id="extensions-options-dialog"> <template> @@ -40,5 +40,5 @@ </div> </dialog> </template> - <script src="chrome://extensions/options_dialog.js"></script> + <script src="options_dialog.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/pack_dialog.html b/chromium/chrome/browser/resources/md_extensions/pack_dialog.html index c793c6364c8..2e11adbdf66 100644 --- a/chromium/chrome/browser/resources/md_extensions/pack_dialog.html +++ b/chromium/chrome/browser/resources/md_extensions/pack_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_button_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_input_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> @@ -12,13 +13,10 @@ <dom-module id="extensions-pack-dialog"> <template> - <style include="cr-shared-style paper-button-style"> + <style include="cr-shared-style paper-button-style paper-input-style"> .file-input { align-items: center; display: flex; - --paper-input-container-input: { - font-size: inherit; - }; } .file-input paper-input { @@ -67,5 +65,5 @@ </extensions-pack-dialog-alert> </template> </template> - <script src="chrome://extensions/pack_dialog.js"></script> + <script src="pack_dialog.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/pack_dialog.js b/chromium/chrome/browser/resources/md_extensions/pack_dialog.js index 2f8ac643de5..de6f9b3b2e7 100644 --- a/chromium/chrome/browser/resources/md_extensions/pack_dialog.js +++ b/chromium/chrome/browser/resources/md_extensions/pack_dialog.js @@ -6,32 +6,31 @@ cr.define('extensions', function() { 'use strict'; /** @interface */ - function PackDialogDelegate() {} - - PackDialogDelegate.prototype = { + class PackDialogDelegate { /** * Opens a file browser for the user to select the root directory. * @return {Promise<string>} A promise that is resolved with the path the * user selected. */ - choosePackRootDirectory: assertNotReached, + choosePackRootDirectory() {} /** * Opens a file browser for the user to select the private key file. * @return {Promise<string>} A promise that is resolved with the path the * user selected. */ - choosePrivateKeyPath: assertNotReached, + choosePrivateKeyPath() {} /** * Packs the extension into a .crx. * @param {string} rootPath * @param {string} keyPath * @param {number=} flag - * @param {function(chrome.developerPrivate.PackDirectoryResponse)=} callback + * @param {function(chrome.developerPrivate.PackDirectoryResponse)=} + * callback */ - packExtension: assertNotReached, - }; + packExtension(rootPath, keyPath, flag, callback) {} + } const PackDialog = Polymer({ is: 'extensions-pack-dialog', diff --git a/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html b/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html index 81810d8bcad..11db084bd02 100644 --- a/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html +++ b/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html @@ -29,5 +29,5 @@ </div> </dialog> </template> - <script src="chrome://extensions/pack_dialog_alert.js"></script> + <script src="pack_dialog_alert.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/service.html b/chromium/chrome/browser/resources/md_extensions/service.html index dd8617e16f9..91b8058c5a1 100644 --- a/chromium/chrome/browser/resources/md_extensions/service.html +++ b/chromium/chrome/browser/resources/md_extensions/service.html @@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<link rel="import" href="chrome://extensions/service.html"> -<link rel="import" href="chrome://extensions/item.html"> -<link rel="import" href="chrome://extensions/navigation_helper.html"> -<script src="chrome://extensions/service.js"></script> +<link rel="import" href="service.html"> +<link rel="import" href="strings.html"> +<link rel="import" href="item.html"> +<link rel="import" href="navigation_helper.html"> +<script src="service.js"></script> diff --git a/chromium/chrome/browser/resources/md_extensions/service.js b/chromium/chrome/browser/resources/md_extensions/service.js index a7c09cb72bf..912dd3e9f54 100644 --- a/chromium/chrome/browser/resources/md_extensions/service.js +++ b/chromium/chrome/browser/resources/md_extensions/service.js @@ -29,6 +29,14 @@ cr.define('extensions', function() { this.manager_ = manager; this.manager_.set('delegate', this); + // Skip any setup or backend requests if we're in guest-mode. + // TODO(scottchen): there might be a better place to do this once manager + // and service become less coupled. + if (loadTimeData.getBoolean('isGuest')) { + this.manager_.initPage(); + return; + } + const keyboardShortcuts = this.manager_.keyboardShortcuts; keyboardShortcuts.addEventListener( 'shortcut-updated', this.onExtensionCommandUpdated_.bind(this)); @@ -317,6 +325,11 @@ cr.define('extensions', function() { }); }); } + + /** @override */ + openDevTools(args) { + chrome.developerPrivate.openDevTools(args); + } } cr.addSingletonGetter(Service); diff --git a/chromium/chrome/browser/resources/md_extensions/shortcut_input.html b/chromium/chrome/browser/resources/md_extensions/shortcut_input.html index 5535af64475..faf98ef6f44 100644 --- a/chromium/chrome/browser/resources/md_extensions/shortcut_input.html +++ b/chromium/chrome/browser/resources/md_extensions/shortcut_input.html @@ -2,16 +2,18 @@ <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_input_style_css.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.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> -<link rel="import" href="chrome://extensions/shortcut_util.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> +<link rel="import" href="shortcut_util.html"> <dom-module id="extensions-shortcut-input"> <template> - <style include="cr-icons cr-hidden-style"> + <style include="cr-icons cr-hidden-style paper-input-style"> #main { position: relative; width: 200px; @@ -19,13 +21,10 @@ #input { --paper-input-container: { - margin-bottom: 12px; + margin-bottom: 0px; + margin-top: 2px; /* Offset underline spacing. */ padding: 0; - }; - --paper-input-container-color: var(--paper-grey-400); - --paper-input-container-focus-color: var(--google-blue-500); - --paper-input-container-input: { - font-size: inherit; + @apply(--cr-primary-text); }; } @@ -38,7 +37,8 @@ </style> <div id="main"> <paper-input id="input" placeholder="$i18n{shortcutTypeAShortcut}" - value="[[computeText_(capturing_, shortcut, pendingShortcut_)]]"> + value="[[computeText_(capturing_, shortcut, pendingShortcut_)]]" + no-label-float> </paper-input> <button id="clear" is="paper-icon-button-light" class="icon-clear no-overlap" on-tap="onClearTap_" @@ -46,5 +46,5 @@ </button> </div> </template> - <script src="chrome://extensions/shortcut_input.js"></script> + <script src="shortcut_input.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/shortcut_input.js b/chromium/chrome/browser/resources/md_extensions/shortcut_input.js index f2a834ece85..a6abd7b3e75 100644 --- a/chromium/chrome/browser/resources/md_extensions/shortcut_input.js +++ b/chromium/chrome/browser/resources/md_extensions/shortcut_input.js @@ -143,9 +143,9 @@ cr.define('extensions', function() { * @private */ computeText_: function() { - if (this.capturing_) - return this.pendingShortcut_; - return this.shortcut; + let shortcutString = + this.capturing_ ? this.pendingShortcut_ : this.shortcut; + return shortcutString.split('+').join(' + '); }, /** diff --git a/chromium/chrome/browser/resources/extensions/shortcut_util.html b/chromium/chrome/browser/resources/md_extensions/shortcut_util.html index 6a187de26c9..e44359faac7 100644 --- a/chromium/chrome/browser/resources/extensions/shortcut_util.html +++ b/chromium/chrome/browser/resources/md_extensions/shortcut_util.html @@ -1,3 +1,3 @@ <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<script src="chrome://extensions/shortcut_util.js"></script> +<script src="shortcut_util.js"></script> diff --git a/chromium/chrome/browser/resources/extensions/shortcut_util.js b/chromium/chrome/browser/resources/md_extensions/shortcut_util.js index 7467307ea94..75f235cedbb 100644 --- a/chromium/chrome/browser/resources/extensions/shortcut_util.js +++ b/chromium/chrome/browser/resources/md_extensions/shortcut_util.js @@ -5,10 +5,8 @@ cr.define('extensions', function() { 'use strict'; - /** - * @enum {number} - */ - var Key = { + /** @enum {number} */ + const Key = { Comma: 188, Del: 46, Down: 40, @@ -34,10 +32,7 @@ cr.define('extensions', function() { * Enum for whether we require modifiers of a keycode. * @enum {number} */ - var ModifierPolicy = { - NOT_ALLOWED: 0, - REQUIRED: 1 - }; + const ModifierPolicy = {NOT_ALLOWED: 0, REQUIRED: 1}; /** * Gets the ModifierPolicy. Currently only "MediaNextTrack", "MediaPrevTrack", @@ -67,11 +62,10 @@ cr.define('extensions', function() { */ function hasModifier(e, countShiftAsModifier) { return e.ctrlKey || e.altKey || - // Meta key is only relevant on Mac and CrOS, where we treat Command - // and Search (respectively) as modifiers. - (cr.isMac && e.metaKey) || - (cr.isChromeOS && e.metaKey) || - (countShiftAsModifier && e.shiftKey); + // Meta key is only relevant on Mac and CrOS, where we treat Command + // and Search (respectively) as modifiers. + (cr.isMac && e.metaKey) || (cr.isChromeOS && e.metaKey) || + (countShiftAsModifier && e.shiftKey); } /** @@ -82,12 +76,12 @@ cr.define('extensions', function() { function isValidKeyCode(keyCode) { if (keyCode == Key.Escape) return false; - for (var k in Key) { + for (let k in Key) { if (Key[k] == keyCode) return true; } return (keyCode >= 'A'.charCodeAt(0) && keyCode <= 'Z'.charCodeAt(0)) || - (keyCode >= '0'.charCodeAt(0) && keyCode <= '9'.charCodeAt(0)); + (keyCode >= '0'.charCodeAt(0) && keyCode <= '9'.charCodeAt(0)); } /** @@ -97,7 +91,7 @@ cr.define('extensions', function() { * @return {string} The keystroke as a string. */ function keystrokeToString(e) { - var output = []; + let output = []; // TODO(devlin): Should this be i18n'd? if (cr.isMac && e.metaKey) output.push('Command'); @@ -110,7 +104,7 @@ cr.define('extensions', function() { if (e.shiftKey) output.push('Shift'); - var keyCode = e.keyCode; + let keyCode = e.keyCode; if (isValidKeyCode(keyCode)) { if ((keyCode >= 'A'.charCodeAt(0) && keyCode <= 'Z'.charCodeAt(0)) || (keyCode >= '0'.charCodeAt(0) && keyCode <= '9'.charCodeAt(0))) { @@ -118,41 +112,59 @@ cr.define('extensions', function() { } else { switch (keyCode) { case Key.Comma: - output.push('Comma'); break; + output.push('Comma'); + break; case Key.Del: - output.push('Delete'); break; + output.push('Delete'); + break; case Key.Down: - output.push('Down'); break; + output.push('Down'); + break; case Key.End: - output.push('End'); break; + output.push('End'); + break; case Key.Home: - output.push('Home'); break; + output.push('Home'); + break; case Key.Ins: - output.push('Insert'); break; + output.push('Insert'); + break; case Key.Left: - output.push('Left'); break; + output.push('Left'); + break; case Key.MediaNextTrack: - output.push('MediaNextTrack'); break; + output.push('MediaNextTrack'); + break; case Key.MediaPlayPause: - output.push('MediaPlayPause'); break; + output.push('MediaPlayPause'); + break; case Key.MediaPrevTrack: - output.push('MediaPrevTrack'); break; + output.push('MediaPrevTrack'); + break; case Key.MediaStop: - output.push('MediaStop'); break; + output.push('MediaStop'); + break; case Key.PageDown: - output.push('PageDown'); break; + output.push('PageDown'); + break; case Key.PageUp: - output.push('PageUp'); break; + output.push('PageUp'); + break; case Key.Period: - output.push('Period'); break; + output.push('Period'); + break; case Key.Right: - output.push('Right'); break; + output.push('Right'); + break; case Key.Space: - output.push('Space'); break; + output.push('Space'); + break; case Key.Tab: - output.push('Tab'); break; + output.push('Tab'); + break; case Key.Up: - output.push('Up'); break; + output.push('Up'); + break; } } } diff --git a/chromium/chrome/browser/resources/md_extensions/sidebar.html b/chromium/chrome/browser/resources/md_extensions/sidebar.html index 030c5c10243..bd1887dd6fd 100644 --- a/chromium/chrome/browser/resources/md_extensions/sidebar.html +++ b/chromium/chrome/browser/resources/md_extensions/sidebar.html @@ -50,7 +50,7 @@ width: 15px; } </style> - <paper-menu id="section-menu" selected="0"> + <paper-menu id="section-menu" selected="{{selected_}}"> <paper-item class="section-item" id="sections-extensions" on-tap="onExtensionsTap_"> <span>$i18n{sidebarExtensions}</span> @@ -70,5 +70,5 @@ <button class="open-in-new" is="paper-icon-button-light"></button> </a> </template> - <script src="chrome://extensions/sidebar.js"></script> + <script src="sidebar.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/sidebar.js b/chromium/chrome/browser/resources/md_extensions/sidebar.js index c81d9193804..a2fea0dfc20 100644 --- a/chromium/chrome/browser/resources/md_extensions/sidebar.js +++ b/chromium/chrome/browser/resources/md_extensions/sidebar.js @@ -5,7 +5,17 @@ cr.define('extensions', function() { const Sidebar = Polymer({ is: 'extensions-sidebar', - behaviors: [I18nBehavior], + properties: { + /** @private {number} */ + selected_: { + type: Number, + value: -1, + }, + }, + + hostAttributes: { + role: 'navigation', + }, /** @private */ onExtensionsTap_: function() { @@ -23,6 +33,30 @@ cr.define('extensions', function() { onKeyboardShortcutsTap_: function() { extensions.navigation.navigateTo({page: Page.SHORTCUTS}); }, + + /** + * @param {!PageState} state + */ + updateSelected: function(state) { + let selected; + + switch (state.page) { + case Page.LIST: + if (state.type == extensions.ShowingType.APPS) + selected = 1; + else + selected = 0; + break; + case Page.SHORTCUTS: + selected = 2; + break; + default: + selected = -1; + break; + } + + this.selected_ = selected; + }, }); return {Sidebar: Sidebar}; diff --git a/chromium/chrome/browser/resources/md_extensions/strings.html b/chromium/chrome/browser/resources/md_extensions/strings.html index ada163e717b..cdc7c9b2a5b 100644 --- a/chromium/chrome/browser/resources/md_extensions/strings.html +++ b/chromium/chrome/browser/resources/md_extensions/strings.html @@ -1,2 +1,2 @@ <link rel="import" href="chrome://resources/html/load_time_data.html"> -<script src="chrome://extensions/strings.js"></script> +<script src="strings.js"></script> diff --git a/chromium/chrome/browser/resources/md_extensions/toolbar.html b/chromium/chrome/browser/resources/md_extensions/toolbar.html index 20d8d952a80..c57b165e06f 100644 --- a/chromium/chrome/browser/resources/md_extensions/toolbar.html +++ b/chromium/chrome/browser/resources/md_extensions/toolbar.html @@ -1,19 +1,18 @@ <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/cr_toolbar/cr_toolbar.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> -<link rel="import" href="chrome://resources/cr_elements/paper_toggle_style_css.html"> <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/paper-styles/color.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> -<link rel="import" href="chrome://extensions/icons.html"> +<link rel="import" href="icons.html"> <link rel="stylesheet" href="chrome://resources/css/md_colors.css"> <dom-module id="extensions-toolbar"> <template> - <style include="cr-hidden-style paper-button-style paper-toggle-style"> + <style include="cr-hidden-style paper-button-style"> :host { --toolbar-width: 580px; --toolbar-color: var(--md-toolbar-color); @@ -25,15 +24,11 @@ } /* This toggle needs special styling because it's on blue background. */ - cr-toolbar paper-toggle-button { - --paper-toggle-button-checked-bar: { - @apply(--cr-toggle-bar-size); - opacity: 0.5; - }; - --paper-toggle-button-checked-bar-color: white; - --paper-toggle-button-checked-button-color: white; - --paper-toggle-button-checked-ink-color: white; - --paper-toggle-button-unchecked-ink-color: white; + cr-toolbar cr-toggle { + --cr-toggle-checked-bar-color: white; + --cr-toggle-checked-button-color: white; + --cr-toggle-checked-ink-color: white; + --cr-toggle-unchecked-ink-color: white; } .dev-controls { @@ -66,16 +61,16 @@ -webkit-margin-end: 16px; } </style> - <cr-toolbar class="paper-header" - page-name="$i18n{toolbarTitle}" + <cr-toolbar page-name="$i18n{toolbarTitle}" search-prompt="$i18n{search}" clear-label="$i18n{clearSearch}" - show-menu> - <div class="more-actions"> - <span>$i18n{toolbarDevMode}</span> - <paper-toggle-button id="dev-mode" on-change="onDevModeChange_" - checked="[[inDevMode]]"> - </paper-toggle-button> + menu-label="$i18n{mainMenu}" + show-menu="[[!isGuest]]" show-search="[[!isGuest]]"> + <div class="more-actions" hidden$="[[isGuest]]"> + <span id="devModeLabel">$i18n{toolbarDevMode}</span> + <cr-toggle id="dev-mode" on-change="onDevModeChange_" + checked="[[inDevMode]]" aria-labelledby="devModeLabel"> + </cr-toggle> </div> </cr-toolbar> <div class="dev-controls" hidden$="[[!inDevMode]]"> @@ -89,8 +84,14 @@ <paper-button id="update-now" on-tap="onUpdateNowTap_"> $i18n{toolbarUpdateNow} </paper-button> +<if expr="chromeos"> + <paper-button id="kiosk-extensions" on-tap="onKioskTap_" + hidden$="[[!kioskEnabled]]"> + $i18n{manageKioskApp} + </paper-button> +</if> </div> </div> </template> - <script src="chrome://extensions/toolbar.js"></script> + <script src="toolbar.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/toolbar.js b/chromium/chrome/browser/resources/md_extensions/toolbar.js index f7e5051491f..f973f10ed3e 100644 --- a/chromium/chrome/browser/resources/md_extensions/toolbar.js +++ b/chromium/chrome/browser/resources/md_extensions/toolbar.js @@ -6,21 +6,19 @@ cr.exportPath('extensions'); cr.define('extensions', function() { /** @interface */ - const ToolbarDelegate = function() {}; - - ToolbarDelegate.prototype = { + class ToolbarDelegate { /** * Toggles whether or not the profile is in developer mode. * @param {boolean} inDevMode */ - setProfileInDevMode: assertNotReached, + setProfileInDevMode(inDevMode) {} /** Opens the dialog to load unpacked extensions. */ - loadUnpacked: assertNotReached, + loadUnpacked() {} /** Updates all extensions. */ - updateAllExtensions: assertNotReached, - }; + updateAllExtensions() {} + } const Toolbar = Polymer({ is: 'extensions-toolbar', @@ -35,6 +33,16 @@ cr.define('extensions', function() { type: Boolean, value: false, }, + + isGuest: Boolean, + + // <if expr="chromeos"> + kioskEnabled: Boolean, + // </if> + }, + + hostAttributes: { + role: 'banner', }, /** @private */ @@ -52,6 +60,13 @@ cr.define('extensions', function() { this.fire('pack-tap'); }, + // <if expr="chromeos"> + /** @private */ + onKioskTap_: function() { + this.fire('kiosk-tap'); + }, + // </if> + /** @private */ onUpdateNowTap_: function() { this.delegate.updateAllExtensions(); diff --git a/chromium/chrome/browser/resources/md_extensions/view_manager.html b/chromium/chrome/browser/resources/md_extensions/view_manager.html index 75a6207af57..8b2bf6f82ac 100644 --- a/chromium/chrome/browser/resources/md_extensions/view_manager.html +++ b/chromium/chrome/browser/resources/md_extensions/view_manager.html @@ -22,5 +22,5 @@ </style> <slot name="view"></slot> </template> - <script src="chrome://extensions/view_manager.js"></script> -</dom-module>
\ No newline at end of file + <script src="view_manager.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/md_feedback/OWNERS b/chromium/chrome/browser/resources/md_feedback/OWNERS deleted file mode 100644 index 02b5b4649de..00000000000 --- a/chromium/chrome/browser/resources/md_feedback/OWNERS +++ /dev/null @@ -1 +0,0 @@ -apacible@chromium.org diff --git a/chromium/chrome/browser/resources/md_feedback/compiled_resources2.gyp b/chromium/chrome/browser/resources/md_feedback/compiled_resources2.gyp deleted file mode 100644 index a3b6ac3db1f..00000000000 --- a/chromium/chrome/browser/resources/md_feedback/compiled_resources2.gyp +++ /dev/null @@ -1,22 +0,0 @@ -# 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. -{ - 'targets': [ - { - 'target_name': 'feedback', - 'dependencies': [ - '<(EXTERNS_GYP):chrome_send', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { - 'target_name': 'feedback_container', - 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - ], -} diff --git a/chromium/chrome/browser/resources/md_feedback/feedback.html b/chromium/chrome/browser/resources/md_feedback/feedback.html deleted file mode 100644 index 665dca7b500..00000000000 --- a/chromium/chrome/browser/resources/md_feedback/feedback.html +++ /dev/null @@ -1,17 +0,0 @@ -<!DOCTYPE HTML> -<html dir="$i18n{textdirection}" lang="$i18n{language}"> -<head> - <meta charset="utf-8"> - - <link rel="import" href="chrome://resources/html/cr.html"> - <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> - <link rel="import" href="chrome://resources/html/load_time_data.html"> - <link rel="import" href="chrome://resources/html/util.html"> - <script src="chrome://feedback/strings.js"></script> - <script src="chrome://feedback/feedback.js"></script> - <link rel="import" href="feedback_container.html"> -</head> -<body> - <feedback-container id="container"></feedback-container> -</body> -</html>
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/md_feedback/feedback.js b/chromium/chrome/browser/resources/md_feedback/feedback.js deleted file mode 100644 index 895f9933525..00000000000 --- a/chromium/chrome/browser/resources/md_feedback/feedback.js +++ /dev/null @@ -1,42 +0,0 @@ -// 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. - -var Feedback = {}; - -/** - * API invoked by the browser MdFeedbackWebUIMessageHandler to communicate - * with this UI. - */ -Feedback.UI = class { - /** - * Populates the feedback form with data. - * - * @param {{email: (string|undefined), - * url: (string|undefined)}} data - * Parameters in data: - * email - user's email, if available. - * url - url of the tab the user was on before triggering feedback. - */ - static setData(data) { - $('container').email = data['email']; - $('container').url = data['url']; - } -}; - -/** API invoked by this UI to communicate with the browser WebUI message - * handler. - */ -Feedback.BrowserApi = class { - /** - * Requests data to initialize the WebUI with. - * The data will be returned via Feedback.UI.setData. - */ - static requestData() { - chrome.send('requestData'); - } -}; - -window.addEventListener('DOMContentLoaded', function() { - Feedback.BrowserApi.requestData(); -}); diff --git a/chromium/chrome/browser/resources/md_feedback/feedback_container.html b/chromium/chrome/browser/resources/md_feedback/feedback_container.html deleted file mode 100644 index 2e88d7f6a8d..00000000000 --- a/chromium/chrome/browser/resources/md_feedback/feedback_container.html +++ /dev/null @@ -1,73 +0,0 @@ -<link rel="import" href="chrome://resources/html/polymer.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-checkbox/paper-checkbox.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-textarea.html"> -<dom-module id="feedback-container"> - <template> - <style> - #additional-info { - margin-top: 10px; - } - - #buttons { - display: -webkit-flex; - justify-content: flex-end; - } - - #header { - font-size: 1.25em; - margin: 12px 0; - } - - paper-button { - cursor: pointer; - text-align: center; - } - - paper-checkbox { - --paper-checkbox-size: 14px; - } - - paper-input, - paper-textarea { - --paper-input-container-input: { - font-size: 1.0em; - line-height: 1.0em; - }; - --paper-input-container-label: { - font-size: 1.0em; - line-height: 1.0em; - }; - } - - paper-input-container { - padding: 0; - } - - #submit-button { - color: var(--paper-blue-700); - } - </style> - <div id="header"> - <span>$i18n{headingText}</span> - </div> - <hr> - <paper-textarea label="$i18n{openEndedLabel}"></paper-textarea> - <span id="additional-info">$i18n{additionalInfoLabel}</span> - <paper-input label="$i18n{urlLabel}" value="[[url]]"></paper-input> - <paper-input label="$i18n{emailLabel}" value="[[email]]"></paper-input> - <div> - <paper-checkbox>$i18n{includeScreenshotLabel}</paper-checkbox> - </div> - <div> - <paper-checkbox>$i18n{sendSystemInfoLabel}</paper-checkbox> - </div> - <p id="privacyNote"></p> - <div id="buttons"> - <paper-button>$i18n{cancelButton}</paper-button> - <paper-button id="submit-button">$i18n{sendReportButton}</paper-button> - </div> - </template> - <script src="feedback_container.js"></script> -</dom-module> diff --git a/chromium/chrome/browser/resources/md_feedback/feedback_container.js b/chromium/chrome/browser/resources/md_feedback/feedback_container.js deleted file mode 100644 index 05f7dceeb68..00000000000 --- a/chromium/chrome/browser/resources/md_feedback/feedback_container.js +++ /dev/null @@ -1,36 +0,0 @@ -// 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. - -// This Polymer element is used as a container for all the feedback -// elements. Based on a number of factors, it determines which elements -// to show and what will be submitted to the feedback servers. -Polymer({ - is: 'feedback-container', - - properties: { - /** - * The user's email, if available. - * @type {string|undefined} - */ - email: { - type: String, - }, - - /** - * The URL of the page the user was on before sending feedback. - * @type {string|undefined} - */ - url: { - type: String, - }, - }, - - ready: function() { - // Retrieves the feedback privacy note text, if it exists. On non-official - // branded builds, the string is not defined. - this.$.privacyNote.innerHTML = loadTimeData.valueExists('privacyNote') ? - loadTimeData.getString('privacyNote') : - ''; - }, -}); diff --git a/chromium/chrome/browser/resources/md_history/BUILD.gn b/chromium/chrome/browser/resources/md_history/BUILD.gn index 2625f114634..4930d084a37 100644 --- a/chromium/chrome/browser/resources/md_history/BUILD.gn +++ b/chromium/chrome/browser/resources/md_history/BUILD.gn @@ -1,6 +1,6 @@ -import("../vulcanize.gni") +import("../optimize_webui.gni") -vulcanize("build") { +optimize_webui("build") { host = "history" html_in_files = [ "app.html", diff --git a/chromium/chrome/browser/resources/md_history/app.html b/chromium/chrome/browser/resources/md_history/app.html index 36b380fed20..ddd7280cc46 100644 --- a/chromium/chrome/browser/resources/md_history/app.html +++ b/chromium/chrome/browser/resources/md_history/app.html @@ -49,19 +49,11 @@ } #drop-shadow { - box-shadow: inset 0 5px 6px -3px rgba(0, 0, 0, 0.4); - height: 6px; - left: 0; - opacity: 0; - pointer-events: none; - position: absolute; - right: 0; - top: 0; - transition: opacity 500ms; + @apply(--cr-container-shadow); } :host([toolbar-shadow_]) #drop-shadow { - opacity: 1; + opacity: var(--cr-container-shadow-max-opacity); } </style> <history-query-manager query-state="{{queryState_}}" @@ -79,12 +71,12 @@ querying="[[queryState_.querying]]" search-term="[[queryState_.searchTerm]]" show-menu-promo="[[showMenuPromo_]]" - show-sync-notice="[[showSyncNotice_(hasSyncedResults, selectedPage_)]]" spinner-active="[[shouldShowSpinner_(queryState_.querying, queryState_.incremental, queryState_.searchTerm)]]"> </history-toolbar> + <div id="drop-shadow"></div> <div id="main-container"> <history-side-bar id="content-side-bar" selected-page="{{selectedPage_}}" show-footer="[[showSidebarFooter]]" @@ -109,11 +101,11 @@ </history-synced-device-manager> </template> </iron-pages> - <div id="drop-shadow"></div> </div> <template is="cr-lazy-render" id="drawer"> - <dialog is="cr-drawer" heading="$i18n{title}" swipe-open> + <dialog is="cr-drawer" heading="$i18n{title}" align="$i18n{textdirection}" + swipe-open> <history-side-bar id="drawer-side-bar" class="drawer-content" selected-page="{{selectedPage_}}" show-footer="[[showSidebarFooter]]"> diff --git a/chromium/chrome/browser/resources/md_history/app.js b/chromium/chrome/browser/resources/md_history/app.js index de2d100abbf..0c25e14061a 100644 --- a/chromium/chrome/browser/resources/md_history/app.js +++ b/chromium/chrome/browser/resources/md_history/app.js @@ -76,8 +76,6 @@ Polymer({ // Used to display notices for profile sign-in status. showSidebarFooter: Boolean, - - hasSyncedResults: Boolean, }, listeners: { @@ -155,7 +153,6 @@ Polymer({ /** @private */ onCrToolbarMenuTap_: function() { var drawer = /** @type {!CrDrawerElement} */ (this.$.drawer.get()); - drawer.align = document.documentElement.dir == 'ltr' ? 'left' : 'right'; drawer.toggle(); this.showMenuPromo_ = false; }, @@ -279,16 +276,6 @@ Polymer({ return querying && !incremental && searchTerm != ''; }, - /** - * @param {boolean} hasSyncedResults - * @param {string} selectedPage - * @return {boolean} Whether the (i) synced results notice should be shown. - * @private - */ - showSyncNotice_: function(hasSyncedResults, selectedPage) { - return hasSyncedResults && selectedPage != 'syncedTabs'; - }, - /** @private */ selectedPageChanged_: function() { this.unselectAll(); diff --git a/chromium/chrome/browser/resources/md_history/externs.js b/chromium/chrome/browser/resources/md_history/externs.js index 590c127b8e3..8f7d7c26978 100644 --- a/chromium/chrome/browser/resources/md_history/externs.js +++ b/chromium/chrome/browser/resources/md_history/externs.js @@ -34,7 +34,6 @@ var HistoryEntry; * chrome/browser/ui/webui/browsing_history_handler.cc: * BrowsingHistoryHandler::QueryComplete() * @typedef {{finished: boolean, - * hasSyncedResults: boolean, * term: string}} */ var HistoryQuery; diff --git a/chromium/chrome/browser/resources/md_history/history.js b/chromium/chrome/browser/resources/md_history/history.js index e6c5ff04fea..c9571285a68 100644 --- a/chromium/chrome/browser/resources/md_history/history.js +++ b/chromium/chrome/browser/resources/md_history/history.js @@ -52,16 +52,13 @@ function historyResult(info, results) { /** * Called by the history backend after receiving results and after discovering * the existence of other forms of browsing history. - * @param {boolean} hasSyncedResults Whether there are synced results. * @param {boolean} includeOtherFormsOfBrowsingHistory Whether to include * a sentence about the existence of other forms of browsing history. */ -function showNotification( - hasSyncedResults, includeOtherFormsOfBrowsingHistory) { +function showNotification(includeOtherFormsOfBrowsingHistory) { waitForAppUpgrade().then(function() { var app = /** @type {HistoryAppElement} */ ($('history-app')); app.showSidebarFooter = includeOtherFormsOfBrowsingHistory; - app.hasSyncedResults = hasSyncedResults; }); } diff --git a/chromium/chrome/browser/resources/md_history/history_toolbar.html b/chromium/chrome/browser/resources/md_history/history_toolbar.html index 2a69ab5a559..12fd39cba14 100644 --- a/chromium/chrome/browser/resources/md_history/history_toolbar.html +++ b/chromium/chrome/browser/resources/md_history/history_toolbar.html @@ -37,43 +37,6 @@ cr-toolbar-selection-overlay { --selection-overlay-max-width: var(--card-max-width); } - - /* Info button and dropdown. */ - - #info-button { - /* Additional styles for unresolved <button>. */ - background: none; - border: none; - color: inherit; - height: 32px; - margin: 6px; - outline: none; - padding: 0; - width: 32px; - } - - #info-button-icon { - height: 20px; - width: 20px; - } - - #sync-notice { - @apply(--shadow-elevation-2dp); - background-color: white; - border-radius: 2px; - color: var(--primary-text-color); - overflow: hidden; - padding: 12px 20px; - position: absolute; - right: 24px; - top: 46px; - z-index: 1; - } - - :host-context([dir=rtl]) #sync-notice { - left: 24px; - right: auto; - } </style> <cr-toolbar id="main-toolbar" page-name="$i18n{title}" @@ -87,20 +50,7 @@ menu-promo="$i18n{menuPromo}" close-menu-promo="$i18n{closeMenuPromo}" on-search-changed="onSearchChanged_"> - <div class="more-actions"> - <template is="dom-if" if="[[showSyncNotice]]"> - <button is="paper-icon-button-light" id="info-button" - on-click="onInfoButtonTap_" - aria-label="$i18n{hasSyncedResultsDescription}"> - <iron-icon icon="history:info-outline" id="info-button-icon"> - </iron-icon> - </button> - </template> - </div> </cr-toolbar> - <div id="sync-notice" hidden="[[!syncNoticeVisible_]]"> - $i18nRaw{hasSyncedResults} - </div> <template is="dom-if" if="[[itemsSelected_]]"> <cr-toolbar-selection-overlay delete-label="$i18n{delete}" cancel-label="$i18n{cancel}" diff --git a/chromium/chrome/browser/resources/md_history/history_toolbar.js b/chromium/chrome/browser/resources/md_history/history_toolbar.js index 62da493f7ab..7b70d8ea149 100644 --- a/chromium/chrome/browser/resources/md_history/history_toolbar.js +++ b/chromium/chrome/browser/resources/md_history/history_toolbar.js @@ -41,19 +41,6 @@ Polymer({ reflectToAttribute: true, }, - // Show an (i) button on the right of the toolbar to display a notice about - // synced history. - showSyncNotice: { - type: Boolean, - observer: 'showSyncNoticeChanged_', - }, - - // Sync notice is currently visible. - syncNoticeVisible_: { - type: Boolean, - value: false, - }, - hasMoreResults: Boolean, querying: Boolean, @@ -65,27 +52,6 @@ Polymer({ showMenuPromo: Boolean, }, - /** - * True if the document currently has listeners to dismiss the sync notice, - * which are added when the notice is first opened. - * @private{boolean} - */ - hasDismissListeners_: false, - - /** @private{?function(!Event)} */ - boundOnDocumentClick_: null, - - /** @private{?function(!Event)} */ - boundOnDocumentKeydown_: null, - - /** @override */ - detached: function() { - if (this.hasDismissListeners_) { - document.removeEventListener('click', this.boundOnDocumentClick_); - document.removeEventListener('keydown', this.boundOnDocumentKeydown_); - } - }, - /** @return {CrToolbarSearchFieldElement} */ get searchField() { return /** @type {CrToolbarElement} */ (this.$['main-toolbar']) @@ -125,12 +91,6 @@ Polymer({ } }, - /** @private */ - showSyncNoticeChanged_: function() { - if (!this.showSyncNotice) - this.syncNoticeVisible_ = false; - }, - /** * @param {!CustomEvent} event * @private @@ -139,43 +99,6 @@ Polymer({ this.fire('change-query', {search: event.detail}); }, - /** - * @param {!MouseEvent} e - * @private - */ - onInfoButtonTap_: function(e) { - this.syncNoticeVisible_ = !this.syncNoticeVisible_; - e.stopPropagation(); - - if (this.hasDismissListeners_) - return; - - this.boundOnDocumentClick_ = this.onDocumentClick_.bind(this); - this.boundOnDocumentKeydown_ = this.onDocumentKeydown_.bind(this); - document.addEventListener('click', this.boundOnDocumentClick_); - document.addEventListener('keydown', this.boundOnDocumentKeydown_); - - this.hasDismissListeners_ = true; - }, - - /** - * @param {!Event} e - * @private - */ - onDocumentClick_: function(e) { - if (e.path.indexOf(this.$['sync-notice']) == -1) - this.syncNoticeVisible_ = false; - }, - - /** - * @param {!Event} e - * @private - */ - onDocumentKeydown_: function(e) { - if (e.key == 'Escape') - this.syncNoticeVisible_ = false; - }, - /** @private */ numberOfItemsSelected_: function(count) { return count > 0 ? loadTimeData.getStringF('itemsSelected', count) : ''; diff --git a/chromium/chrome/browser/resources/md_history/icons.html b/chromium/chrome/browser/resources/md_history/icons.html index 7f0f09a9dfe..2714b83b21f 100644 --- a/chromium/chrome/browser/resources/md_history/icons.html +++ b/chromium/chrome/browser/resources/md_history/icons.html @@ -9,7 +9,6 @@ See http://goo.gl/Y1OdAq for instructions on adding additional icons. --> <g id="chevron-left"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path></g> - <g id="info-outline"><path d="M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 9h2V7h-2v2z"></path></g> <g id="today"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z"></path></g> </defs> </svg> diff --git a/chromium/chrome/browser/resources/md_history/shared_style.html b/chromium/chrome/browser/resources/md_history/shared_style.html index d428ea27f5d..32534a40cca 100644 --- a/chromium/chrome/browser/resources/md_history/shared_style.html +++ b/chromium/chrome/browser/resources/md_history/shared_style.html @@ -35,7 +35,6 @@ } .website-icon { - -webkit-margin-end: 16px; background-repeat: no-repeat; background-size: 16px; height: 16px; @@ -43,6 +42,7 @@ } .website-title { + -webkit-margin-start: 16px; color: var(--primary-text-color); overflow: hidden; text-decoration: none; diff --git a/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js b/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js index 81925027a00..7fb1411e47a 100644 --- a/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js +++ b/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js @@ -2565,9 +2565,9 @@ Polymer({ firstRunFlowHeight - issueHeight - searchHeight + searchPadding - sinkListPadding; - // Limit the height of the dialog to five items, including search. + // Limit the height of the dialog to ten items, including search. var sinkItemHeight = 41; - var maxSinkItems = hasSearch ? 4 : 5; + var maxSinkItems = hasSearch ? 9 : 10; this.sinkListMaxHeight_ = Math.min(sinkItemHeight * maxSinkItems, this.sinkListMaxHeight_); if (sinkList) diff --git a/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.css b/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.css index c684eb52a1a..3b9b0631cc1 100644 --- a/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.css +++ b/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.css @@ -23,7 +23,7 @@ width: 90%; } -:host-context([dir='rtl']) #play-pause-volume-controls { +:host-context([dir='rtl']) #play-pause-volume-hangouts-controls { transform: scaleX(-1); } @@ -40,7 +40,7 @@ margin: 0 8px; } -#play-pause-volume-controls { +#play-pause-volume-hangouts-controls { display: block; margin-top: 13px; overflow: hidden; @@ -86,3 +86,26 @@ overflow: hidden; padding: 0.3em 0; } + +#hangouts-local-present-controls { + -webkit-font-smoothing: antialiased; + -webkit-tap-highlight-color: transparent; + cursor: pointer; + display: inline-block; + float: right; + font-family: 'Roboto', 'Noto', sans-serif; + padding-top: 10.5px; + white-space: nowrap; +} + +#hangouts-local-present-checkbox { + --paper-checkbox-checked-color: #1976D2; + --paper-checkbox-vertical-align: top; +}; + +#hangouts-local-present-checkbox-subtitle { + display: block; + font-size: 0.8em; + margin-top: 2px; + width: 249px; +} diff --git a/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.html b/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.html index 5df138f4510..7a7ac5df9d9 100644 --- a/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.html +++ b/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.html @@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/av-icons.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-slider/paper-slider.html"> <dom-module id="route-controls"> <link rel="import" type="css" href="../../media_router_common.css"> @@ -36,7 +37,7 @@ </span> </div> </div> - <div id="play-pause-volume-controls"> + <div id="play-pause-volume-hangouts-controls"> <span id="button-holder" dir="ltr"> <paper-icon-button id="route-play-pause-button" @@ -53,11 +54,10 @@ title="[[getMuteUnmuteTitle_(routeStatus)]]" on-click="onMuteUnmute_"></paper-icon-button> </span> - <span id="volume-holder"> + <span id="volume-holder" hidden="[[!routeStatus.canSetVolume]]"> <paper-slider aria-valuetext$="[[getVolumeSliderValueText_(displayedVolume_)]]" id="route-volume-slider" - hidden="[[!routeStatus.canSetVolume]]" disabled="[[!routeStatus.canSetVolume]]" on-change="onVolumeChangeComplete_" on-immediate-value-change="onVolumeChangeByDragging_" @@ -65,6 +65,21 @@ value="[[displayedVolume_]]" min="0" max="1" step="0.01"></paper-slider> </span> + <div id="hangouts-local-present-controls" + hidden="[[!routeStatus.hangoutsExtraData]]"> + <paper-checkbox + checked="[[hangoutsLocalPresent_]]" + id="hangouts-local-present-checkbox" + on-change="onHangoutsLocalPresentChange_" + tabindex="0"> + <span id='hangouts-local-present-checkbox-title'> + [[i18n('hangoutsLocalPresentTitle')]] + </span> + <span id="hangouts-local-present-checkbox-subtitle"> + [[i18n('hangoutsLocalPresentSubtitle')]] + </span> + </paper-checkbox> + </div> </div> </div> </div> diff --git a/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.js b/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.js index 2ca16a7c75b..00ec83a1893 100644 --- a/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.js +++ b/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls.js @@ -40,6 +40,16 @@ Polymer({ }, /** + * True if the Hangouts route is currently using local present mode. + * Valid for Hangouts routes only. + * @private {boolean} + */ + hangoutsLocalPresent_: { + type: Boolean, + value: false, + }, + + /** * The timestamp for when the initial media status was loaded. * @private {number} */ @@ -97,6 +107,15 @@ Polymer({ }, /** + * The route currently associated with this controller. + * @type {?media_router.Route|undefined} + */ + route: { + type: Object, + observer: 'onRouteUpdated_', + }, + + /** * The timestamp for when the route details view was opened. * @type {number} */ @@ -284,6 +303,15 @@ Polymer({ }, /** + * Called when the "smooth motion" box for Hangouts is changed by the user. + * @param {!{target: !PaperCheckboxElement}} e + * @private + */ + onHangoutsLocalPresentChange_: function(e) { + media_router.browserApi.setHangoutsLocalPresent(e.target.checked); + }, + + /** * Called when the user toggles the mute status of the media. Sends a mute or * unmute command to the browser. * @private @@ -332,14 +360,17 @@ Polymer({ this.timeIncrementsTimeoutId_ = setTimeout(() => this.maybeIncrementCurrentTime_(), 1000); } + this.hangoutsLocalPresent_ = !!newRouteStatus.hangoutsExtraData && + newRouteStatus.hangoutsExtraData.localPresent; }, /** * Called when the route is updated. Updates the description shown if it has * not been provided by status updates. * @param {?media_router.Route} route + * @private */ - onRouteUpdated: function(route) { + onRouteUpdated_: function(route) { if (!route) { this.stopIncrementingCurrentTime_(); } diff --git a/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls_interface.js b/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls_interface.js index 93aefe5db75..53e5a481271 100644 --- a/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls_interface.js +++ b/chromium/chrome/browser/resources/media_router/elements/route_controls/route_controls_interface.js @@ -18,10 +18,3 @@ RouteControlsInterface.prototype.routeStatus; * Resets the route controls. Called when the route details view is closed. */ RouteControlsInterface.prototype.reset = function() {}; - -/** - * Called when the route is updated. Updates the description shown if it has - * not been provided by status updates. - * @param {!media_router.Route} route - */ -RouteControlsInterface.prototype.onRouteUpdated = function(route) {}; diff --git a/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.html b/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.html index bf90749150e..6bc2093cb04 100644 --- a/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.html +++ b/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.html @@ -20,7 +20,8 @@ </template> <template is="dom-if" if="[[shouldShowWebUiControls_(controllerType_)]]"> <route-controls id="route-controls" - route-details-open-time="[[openTime_]]"></route-controls> + route-details-open-time="[[openTime_]]" + route="[[route]]"></route-controls> </template> <div id="route-action-buttons" class="layout"> <paper-button flat class="route-button button" diff --git a/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.js b/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.js index 946193590b8..b260d818c80 100644 --- a/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.js +++ b/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.js @@ -232,8 +232,6 @@ Polymer({ onRouteChange_: function(newRoute) { if (this.controllerType_ !== media_router.ControllerType.WEBUI) { this.updateActivityStatus_(); - } else if (this.$$('route-controls')) { - this.$$('route-controls').onRouteUpdated(newRoute); } }, diff --git a/chromium/chrome/browser/resources/media_router/media_router_browser_api.js b/chromium/chrome/browser/resources/media_router/media_router_browser_api.js index ac132fdb0c8..fe5e3b12e0e 100644 --- a/chromium/chrome/browser/resources/media_router/media_router_browser_api.js +++ b/chromium/chrome/browser/resources/media_router/media_router_browser_api.js @@ -275,6 +275,10 @@ cr.define('media_router.browserApi', function() { chrome.send('seekCurrentMedia', [{time: time}]); } + /** + * Sends a command to open a file dialog and allow the user to choose a local + * media file. + */ function selectLocalMediaFile() { chrome.send('selectLocalMediaFile'); } @@ -299,6 +303,15 @@ cr.define('media_router.browserApi', function() { chrome.send('setCurrentMediaVolume', [{volume: volume}]); } + /** + * Sets the local present mode of the Hangouts associated with the current + * route. + * @param {boolean} localPresent + */ + function setHangoutsLocalPresent(localPresent) { + chrome.send('hangouts.setLocalPresent', [localPresent]); + } + return { acknowledgeFirstRunFlow: acknowledgeFirstRunFlow, actOnIssue: actOnIssue, @@ -331,5 +344,6 @@ cr.define('media_router.browserApi', function() { selectLocalMediaFile: selectLocalMediaFile, setCurrentMediaMute: setCurrentMediaMute, setCurrentMediaVolume: setCurrentMediaVolume, + setHangoutsLocalPresent: setHangoutsLocalPresent }; }); diff --git a/chromium/chrome/browser/resources/media_router/media_router_data.js b/chromium/chrome/browser/resources/media_router/media_router_data.js index 8d147a31f49..d8252460856 100644 --- a/chromium/chrome/browser/resources/media_router/media_router_data.js +++ b/chromium/chrome/browser/resources/media_router/media_router_data.js @@ -250,6 +250,8 @@ cr.define('media_router', function() { * @param {number} duration The route's duration in seconds. * @param {number} currentTime The route's current position in seconds. * Must not be greater than |duration|. + * @param {!{localPresent: boolean}=} hangoutsExtraData Only set for Hangouts + * routes. * @constructor * @struct */ @@ -257,7 +259,8 @@ cr.define('media_router', function() { title = '', description = '', canPlayPause = false, canMute = false, canSetVolume = false, canSeek = false, playState = media_router.PlayState.PLAYING, isPaused = false, - isMuted = false, volume = 0, duration = 0, currentTime = 0) { + isMuted = false, volume = 0, duration = 0, currentTime = 0, + hangoutsExtraData = undefined) { /** @type {string} */ this.title = title; @@ -291,6 +294,9 @@ cr.define('media_router', function() { /** @type {number} */ this.currentTime = currentTime; + + /** @type {!{localPresent: boolean}|undefined} */ + this.hangoutsExtraData = hangoutsExtraData; }; /** diff --git a/chromium/chrome/browser/resources/memory_internals.js b/chromium/chrome/browser/resources/memory_internals.js index ce94b5adf9e..9c2d8ff79b6 100644 --- a/chromium/chrome/browser/resources/memory_internals.js +++ b/chromium/chrome/browser/resources/memory_internals.js @@ -59,19 +59,19 @@ function returnProcessList(data) { for (let proc of processes) { let procId = proc[0]; - let save_button = document.createElement('button'); - save_button.innerText = '\u21e9 Save dump'; - save_button.onclick = () => dumpProcess(procId); + let saveButton = document.createElement('button'); + saveButton.innerText = '\u21e9 Save dump'; + saveButton.onclick = () => dumpProcess(procId); - let report_button = document.createElement('button'); - report_button.innerText = '\uD83D\uDC1E Report'; - report_button.onclick = () => reportProcess(procId); + let reportButton = document.createElement('button'); + reportButton.innerText = '\uD83D\uDC1E Report'; + reportButton.onclick = () => reportProcess(procId); let procIdText = document.createTextNode(procId.toString()); let description = document.createTextNode(proc[1]); addListRow( - table, 'td', [save_button, report_button, procIdText, description]); + table, 'td', [saveButton, reportButton, procIdText, description]); } proclist.appendChild(table); diff --git a/chromium/chrome/browser/resources/net_internals/bandwidth_view.html b/chromium/chrome/browser/resources/net_internals/bandwidth_view.html index 01e804e558e..b64fa64f257 100644 --- a/chromium/chrome/browser/resources/net_internals/bandwidth_view.html +++ b/chromium/chrome/browser/resources/net_internals/bandwidth_view.html @@ -22,22 +22,6 @@ table.borderless-table, </ul> <a href="#proxy">View current proxy configuration</a> - <h4>Recent events</h4> - <div id=data-reduction-proxy-view-events-content> - <table class="styled-table"> - <thead> - <tr> - <th>Time</th> - <th>Action</th> - </tr> - <tr> - <th colspan=2>Details</th> - </tr> - </thead> - <tbody id=data-reduction-proxy-view-events-tbody> - </tbody> - </table> - </div> <h4>Bandwidth Savings</h4> <table class="styled-table" id="bandwidth-stats-table"> <thead> @@ -55,4 +39,20 @@ table.borderless-table, </tr> </tbody> </table> + <h4>Recent events</h4> + <div id=data-reduction-proxy-view-events-content> + <table class="styled-table"> + <thead> + <tr> + <th>Time</th> + <th>Action</th> + </tr> + <tr> + <th colspan=2>Details</th> + </tr> + </thead> + <tbody id=data-reduction-proxy-view-events-tbody> + </tbody> + </table> + </div> </div> diff --git a/chromium/chrome/browser/resources/net_internals/browser_bridge.js b/chromium/chrome/browser/resources/net_internals/browser_bridge.js index 74b6b7f5817..3068a34fe22 100644 --- a/chromium/chrome/browser/resources/net_internals/browser_bridge.js +++ b/chromium/chrome/browser/resources/net_internals/browser_bridge.js @@ -53,7 +53,6 @@ var BrowserBridge = (function() { this.addNetInfoPollableDataHelper( 'altSvcMappings', 'onAltSvcMappingsChanged'); this.addNetInfoPollableDataHelper('quicInfo', 'onQuicInfoChanged'); - this.addNetInfoPollableDataHelper('sdchInfo', 'onSdchInfoChanged'); this.addNetInfoPollableDataHelper( 'httpCacheInfo', 'onHttpCacheInfoChanged'); @@ -640,17 +639,6 @@ var BrowserBridge = (function() { }, /** - * Adds a listener of SDCH information. |observer| will be called - * back when data is received, through: - * - * observer.onSdchInfoChanged(sdchInfo) - */ - addSdchInfoObserver: function(observer, ignoreWhenUnchanged) { - this.pollableDataHelpers_.sdchInfo.addObserver( - observer, ignoreWhenUnchanged); - }, - - /** * If |force| is true, calls all startUpdate functions. Otherwise, just * runs updates with active observers. */ diff --git a/chromium/chrome/browser/resources/net_internals/events_view.css b/chromium/chrome/browser/resources/net_internals/events_view.css index 1183bfb787f..4358a73c9ae 100644 --- a/chromium/chrome/browser/resources/net_internals/events_view.css +++ b/chromium/chrome/browser/resources/net_internals/events_view.css @@ -90,14 +90,6 @@ color: rgb(112, 112, 0); } -#events-view-source-list-tbody .source-filestream { - color: rgb(112, 0, 112); -} - -#events-view-source-list-tbody .source-ipv6-probe-job { - color: rgb(235, 0, 0); -} - #events-view-source-list-tbody .source-none { color: rgb(235, 0, 0); } diff --git a/chromium/chrome/browser/resources/net_internals/index.html b/chromium/chrome/browser/resources/net_internals/index.html index 3fc6f089d81..fec412dbf6e 100644 --- a/chromium/chrome/browser/resources/net_internals/index.html +++ b/chromium/chrome/browser/resources/net_internals/index.html @@ -44,7 +44,6 @@ found in the LICENSE file. <include src="domain_security_policy_view.html"> <include src="events_view.html"> <include src="timeline_view.html"> - <include src="sdch_view.html"> <include src="chromeos_view.html"> </div> diff --git a/chromium/chrome/browser/resources/net_internals/index.js b/chromium/chrome/browser/resources/net_internals/index.js index 8fcb840e658..fdb47b9c72e 100644 --- a/chromium/chrome/browser/resources/net_internals/index.js +++ b/chromium/chrome/browser/resources/net_internals/index.js @@ -45,7 +45,6 @@ // <include src="prerender_view.js"> // <include src="chromeos_view.js"> // <include src="bandwidth_view.js"> -// <include src="sdch_view.js"> document.addEventListener('DOMContentLoaded', function() { MainView.getInstance(); // from main.js diff --git a/chromium/chrome/browser/resources/net_internals/log_view_painter.js b/chromium/chrome/browser/resources/net_internals/log_view_painter.js index 6210fab747b..edb063bed16 100644 --- a/chromium/chrome/browser/resources/net_internals/log_view_painter.js +++ b/chromium/chrome/browser/resources/net_internals/log_view_painter.js @@ -356,12 +356,6 @@ function defaultWriteParameter(key, value, out) { return; } - if (key == 'sdch_problem_code' && typeof value == 'number') { - var valueStr = value + ' (' + sdchProblemCodeToString(value) + ')'; - out.writeArrowKeyValue(key, valueStr); - return; - } - // Otherwise just default to JSON formatting of the value. out.writeArrowKeyValue(key, JSON.stringify(value)); } diff --git a/chromium/chrome/browser/resources/net_internals/main.js b/chromium/chrome/browser/resources/net_internals/main.js index 3036cfb71b1..6b286ea8b9a 100644 --- a/chromium/chrome/browser/resources/net_internals/main.js +++ b/chromium/chrome/browser/resources/net_internals/main.js @@ -20,7 +20,6 @@ var LoadFlag = null; var CertStatusFlag = null; var LoadState = null; var AddressFamily = null; -var SdchProblemCode = null; var DataReductionProxyBypassEventType = null; /** @@ -189,7 +188,6 @@ var MainView = (function() { addTab(AltSvcView); addTab(SpdyView); addTab(QuicView); - addTab(SdchView); addTab(HttpCacheView); addTab(ModulesView); addTab(DomainSecurityPolicyView); @@ -322,7 +320,6 @@ ConstantsObserver.prototype.onReceivedConstants = function(receivedConstants) { QuicRstStreamError = Constants.quicRstStreamError; AddressFamily = Constants.addressFamily; LoadState = Constants.loadState; - SdchProblemCode = Constants.sdchProblemCode; DataReductionProxyBypassEventType = Constants.dataReductionProxyBypassEventType; DataReductionProxyBypassActionType = @@ -401,15 +398,3 @@ function addressFamilyToString(family) { // Strip that prefix since it is redundant and only clutters the output. return str.replace(/^ADDRESS_FAMILY_/, ''); } - -/** - * Returns the name for sdchProblemCode. - * - * Example: sdchProblemCodeToString(5) should return - * "DECODE_BODY_ERROR". - * @param {number} sdchProblemCode The SDCH problem code. - * @return {string} The name of the given problem code. - */ -function sdchProblemCodeToString(sdchProblemCode) { - return getKeyWithValue(SdchProblemCode, sdchProblemCode); -} diff --git a/chromium/chrome/browser/resources/net_internals/quic_view.html b/chromium/chrome/browser/resources/net_internals/quic_view.html index bfbe3540272..670a10ecd79 100644 --- a/chromium/chrome/browser/resources/net_internals/quic_view.html +++ b/chromium/chrome/browser/resources/net_internals/quic_view.html @@ -72,9 +72,6 @@ </span> </td> </tr><tr> - <td>Close Sessions on IP Change</td> - <td><span jscontent="!!$this.close_sessions_on_ip_change"></span></td> - </tr><tr> <td>Disable Bidirectional Streams</td> <td><span jscontent="!!$this.disable_bidirectional_streams"></span></td> </tr><tr> diff --git a/chromium/chrome/browser/resources/net_internals/sdch_view.html b/chromium/chrome/browser/resources/net_internals/sdch_view.html deleted file mode 100644 index 0288d968943..00000000000 --- a/chromium/chrome/browser/resources/net_internals/sdch_view.html +++ /dev/null @@ -1,69 +0,0 @@ -<div id=sdch-view-tab-content class=content-box> - <ul style="margin-top:0"> - <li>SDCH Enabled: - <span jscontent="!!sdch_enabled" id=sdch-view-sdch-enabled></span> - </li> - </ul> - - <p>SDCH Errors: - <a href="#events&q=type:URL_REQUEST%20SDCH_DECODING_ERROR" - style="padding-right:2em">Decoding</a> - <a href="#events&q=type:URL_REQUEST%20SDCH_DICTIONARY_ERROR">Dictionary</a> - </p> - - <p> - <a href="#events&q=type:URL_REQUEST%20SDCH_DICTIONARY_FETCH"> - SDCH Dictionary Fetches - </a> - </p> - - <p> - <a href="#events&q=type:URL_REQUEST%20Avail-Dictionary"> - SDCH Requests - </a> - </p> - - <h4> - Dictionaries loaded: <span jscontent="dictionaries.length"></span> - </h4> - <table class="styled-table"> - <thead> - <tr> - <th>Domain</th> - <th>Path</th> - <th>Ports</th> - <th>Server Hash</th> - <th>Client Hash</th> - <th>Url</th> - </tr> - </thead> - <tbody id=sdch-view-dictionaries-body> - <tr jsselect="dictionaries"> - <td jscontent="domain"></td> - <td jscontent="path"></td> - <td jscontent="$this.ports ? $this.ports.join(', ') : ''"></td> - <td jscontent="server_hash"></td> - <td jscontent="client_hash"></td> - <td jscontent="url"></td> - </tr> - </tbody> - </table> - - <h4>Blacklisted domains</h4> - <table class="styled-table"> - <thead> - <tr> - <th>Domain</th> - <th>Reason</th> - <th>Tries to back off</th> - </tr> - </thead> - <tbody id=sdch-view-blacklist-body> - <tr jsselect="blacklisted"> - <td jscontent="domain"></td> - <td jscontent="sdchProblemCodeToString(reason)"></td> - <td jscontent="tries"></td> - </tr> - </tbody> - </table> -</div> diff --git a/chromium/chrome/browser/resources/net_internals/sdch_view.js b/chromium/chrome/browser/resources/net_internals/sdch_view.js deleted file mode 100644 index 6f5191e4c79..00000000000 --- a/chromium/chrome/browser/resources/net_internals/sdch_view.js +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2014 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. - -// TODO(rdsmith) Note that SDCH has been disabled in Chrome as of M59. -// This code is being retained for examining dumps generated before that -// milestone. It's probably fine to remove it in M64 or later. - -/** - * This view displays information related to SDCH. - * - * Shows loaded dictionaries, blacklisted domains and SDCH errors. - */ -var SdchView = (function() { - 'use strict'; - - // We inherit from DivView. - var superClass = DivView; - - /** - * @constructor - */ - function SdchView() { - assertFirstConstructorCall(SdchView); - - // Call superclass's constructor. - superClass.call(this, SdchView.MAIN_BOX_ID); - - // Register to receive changes to the SDCH info. - g_browser.addSdchInfoObserver(this, false); - } - - SdchView.TAB_ID = 'tab-handle-sdch'; - SdchView.TAB_NAME = 'SDCH'; - SdchView.TAB_HASH = '#sdch'; - - // IDs for special HTML elements in sdch_view.html - SdchView.MAIN_BOX_ID = 'sdch-view-tab-content'; - SdchView.SDCH_ENABLED_SPAN_ID = 'sdch-view-sdch-enabled'; - SdchView.SECURE_SCHEME_SUPPORT_SPAN_ID = 'sdch-view-secure-scheme-support'; - SdchView.BLACKLIST_TBODY_ID = 'sdch-view-blacklist-body'; - SdchView.DICTIONARIES_TBODY_ID = 'sdch-view-dictionaries-body'; - - cr.addSingletonGetter(SdchView); - - SdchView.prototype = { - // Inherit the superclass's methods. - __proto__: superClass.prototype, - - onLoadLogFinish: function(data) { - return this.onSdchInfoChanged(data.sdchInfo); - }, - - onSdchInfoChanged: function(sdchInfo) { - if (!sdchInfo || typeof(sdchInfo.sdch_enabled) === 'undefined') - return false; - var input = new JsEvalContext(sdchInfo); - jstProcess(input, $(SdchView.MAIN_BOX_ID)); - return true; - }, - }; - - return SdchView; -})(); diff --git a/chromium/chrome/browser/resources/net_internals/source_entry.js b/chromium/chrome/browser/resources/net_internals/source_entry.js index 126750c8cfe..bb1db6fcd08 100644 --- a/chromium/chrome/browser/resources/net_internals/source_entry.js +++ b/chromium/chrome/browser/resources/net_internals/source_entry.js @@ -159,16 +159,6 @@ var SourceEntry = (function() { break; } break; - case EventSourceType.FILESTREAM: - this.description_ = e.params.file_name; - break; - case EventSourceType.IPV6_PROBE_JOB: - if (e.type == EventType.IPV6_PROBE_RUNNING && - e.phase == EventPhase.PHASE_END) { - this.description_ = e.params.ipv6_supported ? 'IPv6 Supported' : - 'IPv6 Not Supported'; - } - break; } if (this.description_ == undefined) diff --git a/chromium/chrome/browser/resources/ntp4/md_incognito_tab.css b/chromium/chrome/browser/resources/ntp4/md_incognito_tab.css index 2bbea6b536b..3b52699d588 100644 --- a/chromium/chrome/browser/resources/ntp4/md_incognito_tab.css +++ b/chromium/chrome/browser/resources/ntp4/md_incognito_tab.css @@ -139,7 +139,6 @@ html[dir=rtl] .bulletpoints + .bulletpoints { /* On narrow screens, align everything to the left. */ @media (max-width: 720px) { .content { - -webkit-margin-start: 0; max-width: 600px !important; /* must override the rule set by JS which * is only valid for width > 720px cases. */ text-align: start; diff --git a/chromium/chrome/browser/resources/ntp4/new_tab.js b/chromium/chrome/browser/resources/ntp4/new_tab.js index cecee13c5a3..bcf8a7b028b 100644 --- a/chromium/chrome/browser/resources/ntp4/new_tab.js +++ b/chromium/chrome/browser/resources/ntp4/new_tab.js @@ -57,12 +57,10 @@ cr.define('ntp', function() { function NewTabView() { var pageSwitcherStart; var pageSwitcherEnd; - if (loadTimeData.getValue('showApps')) { - pageSwitcherStart = /** @type {!ntp.PageSwitcher} */ ( - getRequiredElement('page-switcher-start')); - pageSwitcherEnd = /** @type {!ntp.PageSwitcher} */ ( - getRequiredElement('page-switcher-end')); - } + pageSwitcherStart = /** @type {!ntp.PageSwitcher} */ ( + getRequiredElement('page-switcher-start')); + pageSwitcherEnd = /** @type {!ntp.PageSwitcher} */ ( + getRequiredElement('page-switcher-end')); this.initialize( getRequiredElement('page-list'), getRequiredElement('dot-list'), getRequiredElement('card-slider-frame'), getRequiredElement('trash'), @@ -77,18 +75,15 @@ cr.define('ntp', function() { * Invoked at startup once the DOM is available to initialize the app. */ function onLoad() { - sectionsToWaitFor = 0; - if (loadTimeData.getBoolean('showApps')) { - sectionsToWaitFor++; - if (loadTimeData.getBoolean('showAppLauncherPromo')) { - $('app-launcher-promo-close-button') - .addEventListener('click', function() { - chrome.send('stopShowingAppLauncherPromo'); - }); - $('apps-promo-learn-more').addEventListener('click', function() { - chrome.send('onLearnMore'); - }); - } + sectionsToWaitFor = 1; + if (loadTimeData.getBoolean('showAppLauncherPromo')) { + $('app-launcher-promo-close-button') + .addEventListener('click', function() { + chrome.send('stopShowingAppLauncherPromo'); + }); + $('apps-promo-learn-more').addEventListener('click', function() { + chrome.send('onLearnMore'); + }); } measureNavDots(); diff --git a/chromium/chrome/browser/resources/ntp4/page_list_view.js b/chromium/chrome/browser/resources/ntp4/page_list_view.js index 470dbab409f..89bb392b521 100644 --- a/chromium/chrome/browser/resources/ntp4/page_list_view.js +++ b/chromium/chrome/browser/resources/ntp4/page_list_view.js @@ -162,9 +162,6 @@ cr.define('ntp', function() { this.shownPageIndex = loadTimeData.getInteger('shown_page_index'); - // TODO(dbeam): remove showApps and everything that says if (apps). - assert(loadTimeData.getBoolean('showApps')); - // Request data on the apps so we can fill them in. // Note that this is kicked off asynchronously. 'getAppsCallback' will // be invoked at some point after this function returns. @@ -261,8 +258,6 @@ cr.define('ntp', function() { * position indices. */ appMoved: function(appData) { - assert(loadTimeData.getBoolean('showApps')); - var app = /** @type {ntp.App} */ ($(appData.id)); assert(app, 'trying to move an app that doesn\'t exist'); app.remove(false); @@ -280,8 +275,6 @@ cr.define('ntp', function() { * @param {boolean} fromPage True if the removal was from the current page. */ appRemoved: function(appData, isUninstall, fromPage) { - assert(loadTimeData.getBoolean('showApps')); - var app = /** @type {ntp.App} */ ($(appData.id)); assert(app, 'trying to remove an app that doesn\'t exist'); @@ -316,8 +309,6 @@ cr.define('ntp', function() { * An object with all the data on available applications. */ getAppsCallback: function(data) { - assert(loadTimeData.getBoolean('showApps')); - var startTime = Date.now(); // Remember this to select the correct card when done rebuilding. @@ -423,8 +414,6 @@ cr.define('ntp', function() { * be highlighted. */ appAdded: function(appData, opt_highlight) { - assert(loadTimeData.getBoolean('showApps')); - if (appData.id == this.highlightAppId) { opt_highlight = true; this.highlightAppId = null; @@ -459,8 +448,6 @@ cr.define('ntp', function() { * applications. */ appsPrefChangedCallback: function(data) { - assert(loadTimeData.getBoolean('showApps')); - for (var i = 0; i < data.apps.length; ++i) { $(data.apps[i].id).appData = data.apps[i]; } @@ -500,10 +487,8 @@ cr.define('ntp', function() { 0, Math.min(this.cardSlider.currentCard, this.tilePages.length - 1)); this.cardSlider.setCards( Array.prototype.slice.call(this.tilePages), pageNo); - if (loadTimeData.getBoolean('showApps')) { - this.cardSlider.selectCardByValue(this.appsPages[Math.min( - this.shownPageIndex, this.appsPages.length - 1)]); - } + this.cardSlider.selectCardByValue(this.appsPages[Math.min( + this.shownPageIndex, this.appsPages.length - 1)]); }, /** @@ -511,12 +496,10 @@ cr.define('ntp', function() { * of a moving or insert tile. */ enterRearrangeMode: function() { - if (loadTimeData.getBoolean('showApps')) { - var tempPage = new ntp.AppsPage(); - tempPage.classList.add('temporary'); - var pageName = loadTimeData.getString('appDefaultPageName'); - this.appendTilePage(tempPage, pageName, true); - } + var tempPage = new ntp.AppsPage(); + tempPage.classList.add('temporary'); + var pageName = loadTimeData.getString('appDefaultPageName'); + this.appendTilePage(tempPage, pageName, true); if (ntp.getCurrentlyDraggingTile().firstChild.canBeRemoved()) { $('footer').classList.add('showing-trash-mode'); diff --git a/chromium/chrome/browser/resources/offline_pages/offline_internals.html b/chromium/chrome/browser/resources/offline_pages/offline_internals.html index 6aae5794445..2590edbda99 100644 --- a/chromium/chrome/browser/resources/offline_pages/offline_internals.html +++ b/chromium/chrome/browser/resources/offline_pages/offline_internals.html @@ -112,6 +112,10 @@ <button id="cancel-nwake">Cancel NWake</button> </div> <div> + <!-- This button is used by UI automation testing !--> + <button id="show-notification">Show Notification</button> + </div> + <div> <input id="generate-urls" type="text" placeholder="http://www.url1.com, http://www.url2.com, ..."> <button id="generate-page-bundle">Generate Page Bundle</button> diff --git a/chromium/chrome/browser/resources/offline_pages/offline_internals.js b/chromium/chrome/browser/resources/offline_pages/offline_internals.js index dc5894047d6..2605b0b781a 100644 --- a/chromium/chrome/browser/resources/offline_pages/offline_internals.js +++ b/chromium/chrome/browser/resources/offline_pages/offline_internals.js @@ -160,6 +160,16 @@ cr.define('offlineInternals', function() { } /** + * Error callback for prefetch actions. + * @param {*} error The error that resulted from the prefetch call. + */ + function prefetchResultError(error) { + var errorText = error && error.message ? error.message : error; + + $('prefetch-actions-info').textContent = 'Error: ' + errorText; + } + + /** * Downloads all the stored page and request queue information into a file. * TODO(chili): Create a CSV writer that can abstract out the line joining. */ @@ -295,18 +305,27 @@ cr.define('offlineInternals', function() { } }; $('schedule-nwake').onclick = function() { - browserProxy.scheduleNwake().then(setPrefetchResult); + browserProxy.scheduleNwake() + .then(setPrefetchResult) + .catch(prefetchResultError); }; $('cancel-nwake').onclick = function() { - browserProxy.cancelNwake().then(setPrefetchResult); + browserProxy.cancelNwake() + .then(setPrefetchResult) + .catch(prefetchResultError); + }; + $('show-notification').onclick = function() { + browserProxy.showPrefetchNotification().then(setPrefetchResult); }; $('generate-page-bundle').onclick = function() { browserProxy.generatePageBundle($('generate-urls').value) - .then(setPrefetchResult); + .then(setPrefetchResult) + .catch(prefetchResultError); }; $('get-operation').onclick = function() { browserProxy.getOperation($('operation-name').value) - .then(setPrefetchResult); + .then(setPrefetchResult) + .catch(prefetchResultError); }; $('download-archive').onclick = function() { browserProxy.downloadArchive($('download-name').value); diff --git a/chromium/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js b/chromium/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js index 14777fc803d..b30b277609c 100644 --- a/chromium/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js +++ b/chromium/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js @@ -123,18 +123,28 @@ cr.define('offlineInternals', function() { getNetworkStatus: function() {}, /** - * Schedules the default NWake task. - * @return {!Promise} A promise firing when the task has been scheduled. + * Schedules the default NWake task. The returned Promise will reject if + * there is an error while scheduling. + * @return {!Promise<string>} A promise firing when the task has been + * scheduled. */ scheduleNwake: function() {}, /** * Cancels NWake task. - * @return {!Promise} A promise firing when the task has been cancelled. + * @return {!Promise} A promise firing when the task has been cancelled. The + * returned Promise will reject if there is an error. */ cancelNwake: function() {}, /** + * Shows the prefetching notification with an example origin. + * @return {!Promise<string>} A promise firing when the notification has + * been shown. + */ + showPrefetchNotification: function() {}, + + /** * Sends and processes a request to generate page bundle. * @param {string} urls A list of comma-separated URLs. * @return {!Promise<string>} A string describing the result. @@ -229,6 +239,11 @@ cr.define('offlineInternals', function() { }, /** @override */ + showPrefetchNotification: function() { + return cr.sendWithPromise('showPrefetchNotification'); + }, + + /** @override */ generatePageBundle: function(urls) { return cr.sendWithPromise('generatePageBundle', urls); }, diff --git a/chromium/chrome/browser/resources/vulcanize.gni b/chromium/chrome/browser/resources/optimize_webui.gni index 5555d35f712..e3eb6834391 100644 --- a/chromium/chrome/browser/resources/vulcanize.gni +++ b/chromium/chrome/browser/resources/optimize_webui.gni @@ -25,15 +25,15 @@ template("node") { } } -template("vulcanize") { +template("optimize_webui") { node(target_name) { - script = "//chrome/browser/resources/vulcanize_gn.py" + script = "//chrome/browser/resources/optimize_webui.py" inputs = [ "//chrome/browser/resources/unpack_pak.py", ] - # This depfile is generated by vulcanize_gn.py + # This depfile is generated by optimize_webui.py depfile = "${target_gen_dir}/${target_name}.d" outputs = [] @@ -110,7 +110,7 @@ template("unpak") { ] outputs = [ - "$target_gen_dir/${invoker.out_folder}", + "$target_gen_dir/${invoker.out_folder}/unpack.stamp", ] deps = invoker.deps diff --git a/chromium/chrome/browser/resources/vulcanize_gn.py b/chromium/chrome/browser/resources/optimize_webui.py index f5c48c88d7e..b06fe52fe1c 100755 --- a/chromium/chrome/browser/resources/vulcanize_gn.py +++ b/chromium/chrome/browser/resources/optimize_webui.py @@ -127,7 +127,7 @@ def _update_dep_file(in_folder, args, manifest): f.write(deps_file_header + ': ' + ' '.join(deps)) -def _vulcanize(in_folder, args): +def _optimize(in_folder, args): in_path = os.path.normpath(os.path.join(_CWD, in_folder)) out_path = os.path.join(_CWD, args.out_folder) manifest_out_path = _request_list_path(out_path, args.host) @@ -232,9 +232,9 @@ def main(argv): args.input = os.path.normpath(args.input) args.out_folder = os.path.normpath(args.out_folder) - manifest_out_path = _vulcanize(args.input, args) + manifest_out_path = _optimize(args.input, args) - # Prior call to _vulcanize() generated an output manifest file, containing + # Prior call to _optimize() generated an output manifest file, containing # information about all files that were bundled. Grab it from there. manifest = json.loads(open(manifest_out_path, 'r').read()) diff --git a/chromium/chrome/browser/resources/vulcanize_gn_test.py b/chromium/chrome/browser/resources/optimize_webui_test.py index 9af9495993d..d8d819b4751 100755 --- a/chromium/chrome/browser/resources/vulcanize_gn_test.py +++ b/chromium/chrome/browser/resources/optimize_webui_test.py @@ -3,17 +3,17 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import optimize_webui import os import shutil import tempfile import unittest -import vulcanize_gn _HERE_DIR = os.path.dirname(__file__) -class VulcanizeGnTest(unittest.TestCase): +class OptimizeWebUiTest(unittest.TestCase): def setUp(self): self._out_folder = None self._tmp_dirs = [] @@ -39,11 +39,11 @@ class VulcanizeGnTest(unittest.TestCase): assert self._out_folder return open(os.path.join(self._out_folder, file_name), 'r').read() - def _run_vulcanize(self, depfile, html_in_file, html_out_file, js_out_file): - # TODO(dbeam): make it possible to _run_vulcanize twice? Is that useful? + def _run_optimize(self, depfile, html_in_file, html_out_file, js_out_file): + # TODO(dbeam): make it possible to _run_optimize twice? Is that useful? assert not self._out_folder self._out_folder = self._create_tmp_dir() - vulcanize_gn.main([ + optimize_webui.main([ '--depfile', os.path.join(self._out_folder,'depfile.d'), '--html_in_file', html_in_file, '--html_out_file', html_out_file, @@ -54,7 +54,7 @@ class VulcanizeGnTest(unittest.TestCase): ]) - def testSimpleVulcanize(self): + def testSimpleOptimize(self): self._write_file_to_src_dir('element.html', '<div>got here!</div>') self._write_file_to_src_dir('element.js', "alert('yay');") self._write_file_to_src_dir('ui.html', ''' @@ -62,10 +62,10 @@ class VulcanizeGnTest(unittest.TestCase): <script src="element.js"></script> ''') - self._run_vulcanize(depfile='depfile.d', - html_in_file='ui.html', - html_out_file='fast.html', - js_out_file='fast.js') + self._run_optimize(depfile='depfile.d', + html_in_file='ui.html', + html_out_file='fast.html', + js_out_file='fast.js') fast_html = self._read_out_file('fast.html') self.assertFalse('element.html' in fast_html) diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js index 1e7164a333f..de21ff24b32 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js @@ -2,6 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** + * The |title| is the text label displayed for the bookmark. + * + * The bookmark may point at a location in the PDF or a URI. + * If it points at a location, |page| indicates which 0-based page it leads to. + * Optionally, |y| is the y position in that page, in pixel coordinates. + * If it points at an URI, |uri| is the target for that bookmark. + * + * |children| is an array of the |Bookmark|s that are below this in a table of + * contents tree + * structure. + * @typedef {{ + * title: string, + * page: number, + * y: number, + * uri: string, + * children: !Array<!Bookmark> + * }} + */ +var Bookmark; + (function() { /** Amount that each level of bookmarks is indented by (px). */ var BOOKMARK_INDENT = 20; @@ -10,12 +31,7 @@ Polymer({ is: 'viewer-bookmark', properties: { - /** - * A bookmark object, each containing a: - * - title - * - page (optional) - * - children (an array of bookmarks) - */ + /** @type {Bookmark} */ bookmark: {type: Object, observer: 'bookmarkChanged_'}, depth: {type: Number, observer: 'depthChanged'}, @@ -48,10 +64,17 @@ Polymer({ }, onClick: function() { - if (this.bookmark.hasOwnProperty('page')) - this.fire('change-page', {page: this.bookmark.page}); - else if (this.bookmark.hasOwnProperty('uri')) + if (this.bookmark.hasOwnProperty('page')) { + if (this.bookmark.hasOwnProperty('y')) { + this.fire( + 'change-page-and-y', + {page: this.bookmark.page, y: this.bookmark.y}); + } else { + this.fire('change-page', {page: this.bookmark.page}); + } + } else if (this.bookmark.hasOwnProperty('uri')) { this.fire('navigate', {uri: this.bookmark.uri, newtab: true}); + } }, onEnter_: function(e) { diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html index bc6553aab56..9ca3d05e7c8 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html @@ -5,7 +5,6 @@ <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animation-runner-behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toolbar/paper-toolbar.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="../icons.html"> <link rel="import" href="../viewer-bookmarks-content/viewer-bookmarks-content.html"> @@ -22,8 +21,8 @@ /* We introduce a wrapper aligner element to help with laying out the main * toolbar content without changing the bottom-aligned progress bar. */ #aligner { - @apply(--layout-horizontal); - @apply(--layout-center); + align-items: center; + display: flex; padding: 0 8px; width: 100%; } @@ -68,11 +67,23 @@ width: 100%; } - paper-toolbar { - --paper-toolbar-background: rgb(50, 54, 57); - --paper-toolbar-height: 48px; + #toolbar { @apply(--shadow-elevation-2dp); + background-color: rgb(50, 54, 57); color: rgb(241, 241, 241); + display: flex; + height: 48px; + padding: 0 16px; + } + + #progress-container { + bottom: 0; + left: 0; + margin: 0; + position: absolute; + right: 0; + top: auto; + width: auto; } .invisible { @@ -103,8 +114,8 @@ } } </style> - <paper-toolbar> - <div id="aligner" class="middle"> + <div id="toolbar"> + <div id="aligner"> <span id="title" title="{{docTitle}}"> <span>{{docTitle}}</span> </span> @@ -145,10 +156,10 @@ </viewer-toolbar-dropdown> </div> </div> - <div class="bottom fit"> + <div id="progress-container"> <paper-progress id="progress" value="{{loadProgress}}"></paper-progress> </div> - </paper-toolbar> + </div> </template> <script src="viewer-pdf-toolbar.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/pdf/pdf.js b/chromium/chrome/browser/resources/pdf/pdf.js index ffc2a4907c9..acb36ab187d 100644 --- a/chromium/chrome/browser/resources/pdf/pdf.js +++ b/chromium/chrome/browser/resources/pdf/pdf.js @@ -103,6 +103,7 @@ function PDFViewer(browserApi) { this.isPrintPreview_ = location.origin === 'chrome://print'; this.isPrintPreviewLoaded_ = false; + this.isUserInitiatedEvent_ = true; // Parse open pdf parameters. this.paramsParser_ = @@ -139,7 +140,8 @@ function PDFViewer(browserApi) { this.viewport_ = new Viewport( window, this.sizer_, this.viewportChanged_.bind(this), this.beforeZoom_.bind(this), this.afterZoom_.bind(this), - getScrollbarWidth(), defaultZoom, topToolbarHeight); + this.setUserInitiated_.bind(this), getScrollbarWidth(), defaultZoom, + topToolbarHeight); // Create the plugin object dynamically so we can set its src. The plugin // element is sized to fill the entire window and is set to be fixed @@ -220,6 +222,10 @@ function PDFViewer(browserApi) { this.viewport_.goToPage(e.detail.page); }); + document.body.addEventListener('change-page-and-y', e => { + this.viewport_.goToPageAndY(e.detail.page, e.detail.y); + }); + document.body.addEventListener('navigate', e => { var disposition = e.detail.newtab ? Navigator.WindowOpenDisposition.NEW_BACKGROUND_TAB : @@ -575,7 +581,9 @@ PDFViewer.prototype = { switch (message.data.type.toString()) { case 'documentDimensions': this.documentDimensions_ = message.data; + this.isUserInitiatedEvent_ = false; this.viewport_.setDocumentDimensions(this.documentDimensions_); + this.isUserInitiatedEvent_ = true; // If we received the document dimensions, the password was good so we // can dismiss the password screen. if (this.passwordScreen_.active) @@ -675,6 +683,7 @@ PDFViewer.prototype = { var pinchPhase = this.viewport_.pinchPhase; this.plugin_.postMessage({ type: 'viewport', + userInitiated: true, zoom: zoom, xOffset: position.x, yOffset: position.y, @@ -697,6 +706,7 @@ PDFViewer.prototype = { this.plugin_.postMessage({ type: 'viewport', + userInitiated: this.isUserInitiatedEvent_, zoom: zoom, xOffset: position.x, yOffset: position.y, @@ -710,6 +720,19 @@ PDFViewer.prototype = { }, /** + * @param {boolean} userInitiated The value to set |isUserInitiatedEvent_| + * to. + * @private + * A callback that sets |isUserInitiatedEvent_| to |userInitiated|. + */ + setUserInitiated_: function(userInitiated) { + if (this.isUserInitiatedEvent_ == userInitiated) { + throw 'Trying to set user initiated to current value.'; + } + this.isUserInitiatedEvent_ = userInitiated; + }, + + /** * @private * A callback that's called when an update to a pinch zoom is detected. * @param {!Object} e the pinch event. @@ -850,7 +873,9 @@ PDFViewer.prototype = { this.loadState_ = LoadState.LOADING; if (!this.inPrintPreviewMode_) { this.inPrintPreviewMode_ = true; + this.isUserInitiatedEvent_ = false; this.zoomToolbar_.forceFitToPage(); + this.isUserInitiatedEvent_ = true; } // Stash the scroll location so that it can be restored when the new diff --git a/chromium/chrome/browser/resources/pdf/viewport.js b/chromium/chrome/browser/resources/pdf/viewport.js index 358922c606b..e82551d1080 100644 --- a/chromium/chrome/browser/resources/pdf/viewport.js +++ b/chromium/chrome/browser/resources/pdf/viewport.js @@ -51,6 +51,8 @@ function frameToPluginCoordinate(coordinateInFrame) { * @param {Function} viewportChangedCallback is run when the viewport changes * @param {Function} beforeZoomCallback is run before a change in zoom * @param {Function} afterZoomCallback is run after a change in zoom + * @param {Function} setUserInitiatedCallback is run to indicate whether a zoom + * event is user initiated. * @param {number} scrollbarWidth the width of scrollbars on the page * @param {number} defaultZoom The default zoom level. * @param {number} topToolbarHeight The number of pixels that should initially @@ -58,12 +60,14 @@ function frameToPluginCoordinate(coordinateInFrame) { */ function Viewport( window, sizer, viewportChangedCallback, beforeZoomCallback, - afterZoomCallback, scrollbarWidth, defaultZoom, topToolbarHeight) { + afterZoomCallback, setUserInitiatedCallback, scrollbarWidth, defaultZoom, + topToolbarHeight) { this.window_ = window; this.sizer_ = sizer; this.viewportChangedCallback_ = viewportChangedCallback; this.beforeZoomCallback_ = beforeZoomCallback; this.afterZoomCallback_ = afterZoomCallback; + this.setUserInitiatedCallback_ = setUserInitiatedCallback; this.allowedToChangeZoom_ = false; this.internalZoom_ = 1; this.zoomManager_ = new InactiveZoomManager(this, 1); @@ -80,7 +84,7 @@ function Viewport( this.firstPinchCenterInFrame_ = null; window.addEventListener('scroll', this.updateViewport_.bind(this)); - window.addEventListener('resize', this.resize_.bind(this)); + window.addEventListener('resize', this.resizeWrapper_.bind(this)); } /** @@ -223,6 +227,16 @@ Viewport.prototype = { /** * @private + * Called when the browser window size changes. + */ + resizeWrapper_: function() { + this.setUserInitiatedCallback_(false); + this.resize_(); + this.setUserInitiatedCallback_(true); + }, + + /** + * @private * Called when the viewport size changes. */ resize_: function() { @@ -305,6 +319,7 @@ Viewport.prototype = { /** * @private + * @param {function} f Function to wrap * Used to wrap a function that might perform zooming on the viewport. This is * required so that we can notify the plugin that zooming is in progress * so that while zooming is taking place it can stop reacting to scroll events @@ -721,6 +736,15 @@ Viewport.prototype = { * @param {number} page the index of the page to go to. zero-based. */ goToPage: function(page) { + this.goToPageAndY(page, 0); + }, + + /** + * Go to the given y position in the given page index. + * @param {number} page the index of the page to go to. zero-based. + * @param {number} y the y position in the page to go to. + */ + goToPageAndY: function(page, y) { this.mightZoom_(() => { if (this.pageDimensions_.length === 0) return; @@ -737,7 +761,7 @@ Viewport.prototype = { toolbarOffset = this.topToolbarHeight_; this.position = { x: dimensions.x * this.zoom, - y: dimensions.y * this.zoom - toolbarOffset + y: (dimensions.y + y) * this.zoom - toolbarOffset }; this.updateViewport_(); }); diff --git a/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json b/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json index f337af402a5..ebd071c247c 100644 --- a/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json +++ b/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json @@ -1,5 +1,5 @@ { - "x-version": 25, + "x-version": 24, "google-talk": { "mime_types": [ ], @@ -80,9 +80,9 @@ ], "versions": [ { - "version": "27.0.0.170", + "version": "26.0.0.126", "status": "up_to_date", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb17-32.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb17-17.html" } ], "lang": "en-US", diff --git a/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json b/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json index fc04e3f285e..efd1b4f3612 100644 --- a/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json +++ b/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json @@ -1,5 +1,5 @@ { - "x-version": 31, + "x-version": 30, "google-talk": { "mime_types": [ ], @@ -115,9 +115,9 @@ ], "versions": [ { - "version": "27.0.0.170", + "version": "26.0.0.126", "status": "requires_authorization", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb17-32.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb17-17.html" } ], "lang": "en-US", diff --git a/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json b/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json index 7dc26f683ac..00786d2f197 100644 --- a/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json +++ b/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json @@ -1,5 +1,5 @@ { - "x-version": 40, + "x-version": 39, "google-talk": { "mime_types": [ ], @@ -137,9 +137,9 @@ ], "versions": [ { - "version": "27.0.0.170", + "version": "26.0.0.126", "status": "requires_authorization", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb17-32.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb17-17.html" } ], "lang": "en-US", diff --git a/chromium/chrome/browser/resources/policy.html b/chromium/chrome/browser/resources/policy.html index 480bdc9d88c..804f44a891e 100644 --- a/chromium/chrome/browser/resources/policy.html +++ b/chromium/chrome/browser/resources/policy.html @@ -6,7 +6,7 @@ <title i18n-content="title"></title> <link rel="stylesheet" href="chrome://resources/css/chrome_shared.css"> -<link rel="stylesheet" href="uber/uber_shared.css"> +<link rel="stylesheet" href="uber_shared.css"> <link rel="stylesheet" href="chrome://policy/policy.css"> <script src="chrome://resources/js/action_link.js"></script> @@ -113,7 +113,9 @@ <div class="source elide"></div> </td> <td class="name-column"> - <div class="name elide"></div> + <div class="name elide"> + <a class="name-link" target="_blank"></a> + </div> </td> <td class="value-column"> <div class="value-container"> diff --git a/chromium/chrome/browser/resources/policy_android.css b/chromium/chrome/browser/resources/policy_android.css index 7bb0e2ac53b..999e02323da 100644 --- a/chromium/chrome/browser/resources/policy_android.css +++ b/chromium/chrome/browser/resources/policy_android.css @@ -167,6 +167,12 @@ div.name { width: 15%; } +.no-help-link .name-link { + color: inherit; + pointer-events: none; + text-decoration: none; +} + div.elide, span.value { overflow: hidden; diff --git a/chromium/chrome/browser/resources/policy_base.js b/chromium/chrome/browser/resources/policy_base.js index 8994b97bea4..8ab679951be 100644 --- a/chromium/chrome/browser/resources/policy_base.js +++ b/chromium/chrome/browser/resources/policy_base.js @@ -128,7 +128,15 @@ cr.define('policy', function() { this.unset = !value; // Populate the name column. - this.querySelector('.name').textContent = name; + this.querySelector('.name-link').textContent = name; + this.querySelector('.name-link').href = + 'https://chromium.org/administrators/policy-list-3#' + name; + this.querySelector('.name-link').title = + loadTimeData.getStringF('policyLearnMore', name); + + if (unknown) { + this.classList.add('no-help-link'); + } // Populate the remaining columns with policy scope, level and value if a // value has been set. Otherwise, leave them blank. @@ -174,6 +182,25 @@ cr.define('policy', function() { } }, + /* + * Get value width of the value container. + * @param {Object} valueContainer Container for the value. + * @private + */ + getValueWidth_: function(valueContainer) { + return valueContainer.querySelector('.value').offsetWidth; + }, + + /* + * Update the value width for the value container if necessary. + * @param {Object} valueContainer Container for the value. + * @private + */ + updateValueWidth_: function(valueContainer) { + if (valueContainer.valueWidth == undefined) { + valueContainer.valueWidth = this.getValueWidth_(valueContainer); + } + }, /** * Check the table columns for overflow. Most columns are automatically * elided when overflow occurs. The only action required is to add a tooltip @@ -193,15 +220,12 @@ cr.define('policy', function() { // This is required to be able to check whether the contents would still // overflow the column once it has been hidden and replaced by a link. var valueContainer = this.querySelector('.value-container'); - if (valueContainer.valueWidth == undefined) { - valueContainer.valueWidth = - valueContainer.querySelector('.value').offsetWidth; - } + this.updateValueWidth_(valueContainer); // Determine whether the contents of the value column overflows. The // visibility of the contents, replacement link and additional row // containing the complete value that depend on this are handled by CSS. - if (valueContainer.offsetWidth < valueContainer.valueWidth) + if (valueContainer.offsetWidth <= valueContainer.valueWidth) this.classList.add('has-overflowed-value'); else this.classList.remove('has-overflowed-value'); @@ -479,6 +503,9 @@ cr.define('policy', function() { appendNewTable: function(id, label_title, label_content) { var newSection = this.createPolicyTableSection(id, label_title, label_content); + if (id != 'chrome') { + newSection.classList.add('no-help-link'); + } this.mainSection.appendChild(newSection); return this.policyTables[id]; }, @@ -521,6 +548,8 @@ cr.define('policy', function() { return section; }, + tableHeadings: ['Scope', 'Level', 'Source', 'Name', 'Value', 'Status'], + /** * Creates a new table for displaying policies. * @return {Element} The newly created table. @@ -529,13 +558,12 @@ cr.define('policy', function() { var newTable = window.document.createElement('table'); var tableHead = window.document.createElement('thead'); var tableRow = window.document.createElement('tr'); - var tableHeadings = - ['Scope', 'Level', 'Source', 'Name', 'Value', 'Status']; - for (var i = 0; i < tableHeadings.length; i++) { + for (var i = 0; i < this.tableHeadings.length; i++) { var tableHeader = window.document.createElement('th'); - tableHeader.classList.add(tableHeadings[i].toLowerCase() + '-column'); + tableHeader.classList.add( + this.tableHeadings[i].toLowerCase() + '-column'); tableHeader.textContent = - loadTimeData.getString('header' + tableHeadings[i]); + loadTimeData.getString('header' + this.tableHeadings[i]); tableRow.appendChild(tableHeader); } tableHead.appendChild(tableRow); diff --git a/chromium/chrome/browser/resources/policy_common.css b/chromium/chrome/browser/resources/policy_common.css index b455641d75a..937c38ea798 100644 --- a/chromium/chrome/browser/resources/policy_common.css +++ b/chromium/chrome/browser/resources/policy_common.css @@ -105,6 +105,12 @@ th { font-weight: normal; } +.no-help-link .name-link { + color: inherit; + pointer-events: none; + text-decoration: none; +} + div.elide, span.value { overflow: hidden; diff --git a/chromium/chrome/browser/resources/policy_tool.css b/chromium/chrome/browser/resources/policy_tool.css new file mode 100644 index 00000000000..7361ccbd141 --- /dev/null +++ b/chromium/chrome/browser/resources/policy_tool.css @@ -0,0 +1,41 @@ +/* Copyright (c) 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. */ + +@import 'policy_common.css'; + +.edit-button { + float: right; + margin: 0; + padding: 0; +} + +tbody:not(.value-editing-on) .value-edit-form { + display: none; +} + +tbody.value-editing-on .save-button { + display: block; + float: right; +} + +tbody.value-editing-on .edit-button, +tbody.value-editing-on .value { + display: none; +} + +#invalid-session-name-error { + color: red; +} + +#invalid-session-name-error:not(hidden) { + display: block; +} + +#session-list { + background: white; +} + +#saving span { + font-weight: bold; +} diff --git a/chromium/chrome/browser/resources/policy_tool.html b/chromium/chrome/browser/resources/policy_tool.html new file mode 100644 index 00000000000..8af70271184 --- /dev/null +++ b/chromium/chrome/browser/resources/policy_tool.html @@ -0,0 +1,100 @@ +<!doctype html> +<html dir="$i18n{textdirection}" lang="$i18n{language}"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, user-scalable=no"> +<title>$i18n{title}</title> + +<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css"> +<link rel="stylesheet" href="uber_shared.css"> +<link rel="stylesheet" href="chrome://policy-tool/policy_tool.css"> + +<script src="chrome://resources/js/action_link.js"></script> +<script src="chrome://resources/js/cr.js"></script> +<script src="chrome://resources/js/cr/ui.js"></script> +<script src="chrome://resources/js/cr/ui/focus_outline_manager.js"></script> +<script src="chrome://resources/js/load_time_data.js"></script> +<script src="chrome://resources/js/util.js"></script> +</head> + +<body> + <div id="filter-overlay" class="page"> + <header> + <input id="filter" class="search-field-container" type="search" + placeholder="$i18n{filterPlaceholder}" + aria-label="$i18n{filterPlaceholder}" incremental> + </input> + </header> + </div> + <div class="page"> + <header> + <h1>$i18n{title}</h1> + </header> + <section class="reload-show-unset-section"> + <form id="session-choice"> + <input id="session-name-field" type="text" autocomplete="off" + placeholder="$i18n{sessionNamePlaceholder}"> + <input type="submit" id="load-session-button" + value="$i18n{loadSession}"> + </form> + <div id="saving" hidden> + <span>$i18n{errorSavingDisabled}</span> + </div> + <div id="show-unset-container" class="show-unset-checkbox"> + <label> + <input id="show-unset" type="checkbox"></input> + <span>$i18n{showUnset}</span> + </label> + </div> + <span id="invalid-session-name-error" hidden> + $i18n{errorInvalidSessionName} + </span> + </section> + <section id="sessions"> + <select size="4" id="session-list"> + </select> + </section> + <section id="main-section" class="empty"> + <!-- This is where policy tables get dynamically added. --> + </section> + <section id="disable-editing-error" hidden> + <span>$i18n{errorFileCorrupted}</span> + <button id="enable-editing">$i18n{enableEditing}</button> + </section> + </div> + <div hidden> + <table> + <tbody id="policy-template"> + <tr> + <td class="name-column"> + <div class="name elide"></div> + </td> + <td class="value-column"> + <div class="value-container"> + <span class="value"></span> + <a is="action-link" class="toggle-expanded-value"></a> + <button class="edit-button">$i18n{edit}</button> + <form class="value-edit-form"> + <input class="value-edit-field" type="text"> + <input type="submit" class="save-button" + value="$i18n{save}"> + </form> + </div> + </td> + <td class="status-column"> + <div class="status elide"></div> + </td> + </tr> + <tr class="expanded-value-container"> + <td class="expanded-value" colspan=3></td> + </tr> + </tbody> + </table> + </div> +</body> + +<script src="chrome://policy-tool/strings.js"></script> +<script src="chrome://resources/js/i18n_template.js"></script> +<script src="chrome://policy-tool/policy_base.js"></script> +<script src="chrome://policy-tool/policy_tool.js"></script> +</html> diff --git a/chromium/chrome/browser/resources/policy_tool.js b/chromium/chrome/browser/resources/policy_tool.js new file mode 100644 index 00000000000..871322846d4 --- /dev/null +++ b/chromium/chrome/browser/resources/policy_tool.js @@ -0,0 +1,273 @@ +// 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. + +/** + * Sets the list of current sessions. + * @param {!Array<string>} sessions List of session names. + */ +policy.Page.setSessionsList = function(sessions) { + var list = $('session-list'); + + // Clear the sessions list. + list.innerHTML = ''; + + // Set the new sessions list. + for (var i = 0; i < sessions.length; ++i) { + var option = document.createElement('OPTION'); + option.value = sessions[i]; + option.textContent = sessions[i]; + list.appendChild(option); + } +}; + +// Override some methods of policy.Page. + +/** + * Shows error message when the session name is invalid. + */ +policy.Page.showInvalidSessionNameError = function() { + $('invalid-session-name-error').hidden = false; +}; + +/** + * Disables editing policy values by hiding the main section and shows an + * error message instead. + */ +policy.Page.disableEditing = function() { + $('disable-editing-error').hidden = false; + $('main-section').hidden = true; +}; + +/** + * Disables saving to disk by hiding the 'load session' form and showing an + * error message instead. + */ +policy.Page.disableSaving = function() { + $('saving').hidden = false; + $('session-choice').hidden = true; +}; + +/** @override */ +policy.Page.setPolicyValues = function(values) { + var page = this.getInstance(); + page.enableEditing(); + var table = page.policyTables['chrome']; + table.setPolicyValues(values.chromePolicies || {}); + if (values.hasOwnProperty('extensionPolicies')) { + for (var extensionId in values.extensionPolicies) { + table = page.policyTables['extension-' + extensionId]; + if (table) { + table.setPolicyValues(values.extensionPolicies[extensionId]); + } + } + } else { + for (var extension in page.policyTables) { + if (extension == 'chrome') { + continue; + } + table = page.policyTables[extension]; + table.setPolicyValues({}); + } + } +}; + +/** @override */ +policy.Page.prototype.initialize = function() { + cr.ui.FocusOutlineManager.forDocument(document); + + this.mainSection = $('main-section'); + this.policyTables = {}; + + // Place the initial focus on the session choice input field. + $('session-name-field').select(); + + $('filter').onsearch = (event) => { + for (policyTable in this.policyTables) { + this.policyTables[policyTable].setFilterPattern($('filter').value); + } + }; + + $('session-choice').onsubmit = () => { + $('invalid-session-name-error').hidden = true; + var session = $('session-name-field').value; + chrome.send('loadSession', [session]); + $('session-name-field').value = ''; + // Return false in order to prevent the browser from reloading the whole + // page. + return false; + }; + + $('show-unset').onchange = () => { + for (policyTable in this.policyTables) { + this.policyTables[policyTable].filter(); + } + }; + + $('enable-editing').onclick = () => { + this.enableEditing(); + chrome.send('resetSession'); + }; + // Notify the browser that the page has loaded, causing it to send the + // list of all known policies and the values from the default session. + chrome.send('initialized'); +}; + +policy.Page.prototype.enableEditing = function() { + $('main-section').hidden = false; + $('disable-editing-error').hidden = true; +}; + +/** + * Extracts current policy values to send to backend for saving. + * @return {Object} The dictionary containing policy values. + */ +policy.Page.prototype.getDictionary = function() { + var result = {chromePolicies: {}, extensionPolicies: {}}; + for (var id in this.policyTables) { + if (id == 'chrome') { + result.chromePolicies = this.policyTables[id].getDictionary(); + } else { + const PREFIX_LENGTH = 'extension-'.length; + var extensionId = id.substr(PREFIX_LENGTH); + result.extensionPolicies[extensionId] = + this.policyTables[id].getDictionary(); + } + } + return result; +}; + +// Specify necessary columns. +policy.Page.prototype.tableHeadings = ['Name', 'Value', 'Status']; + +// Override policy.Policy methods. + +/** @override */ +policy.Policy.prototype.decorate = function() { + this.updateToggleExpandedValueText_(); + this.querySelector('.edit-button') + .addEventListener('click', this.onValueEditing_.bind(this)); + this.querySelector('.value-edit-form').onsubmit = + this.submitEditedValue_.bind(this); + this.querySelector('.toggle-expanded-value') + .addEventListener('click', this.toggleExpandedValue_.bind(this)); +}; + +/** @override */ +policy.Policy.prototype.initialize = function(name, value, unknown) { + this.name = name; + this.unset = !value; + this.unknown = unknown; + this.querySelector('.name').textContent = name; + if (value) { + this.setValue_(value.value); + } + this.setStatus_(value); +}; + +/** + * Set the status column. + * @param {Object} value Dictionary with information about the policy value. + * @private + */ +policy.Policy.prototype.setStatus_ = function(value) { + var status; + if (this.unknown) { + status = loadTimeData.getString('unknown'); + } else if (!value) { + status = loadTimeData.getString('unset'); + } else if (value.error) { + status = value.error; + } else { + status = loadTimeData.getString('ok'); + } + this.querySelector('.status').textContent = status; +}; + +/** + * Set the policy value. + * @param {Object|string|integer|boolean} value Policy value. + * @private + */ +policy.Policy.prototype.setValue_ = function(value) { + this.value = value; + if (!value) { + value = ''; + } else if (typeof value != 'string') { + value = JSON.stringify(value); + } + this.unset = !value; + this.querySelector('.value').textContent = value; + this.querySelector('.expanded-value').textContent = value; + this.querySelector('.value-edit-field').value = value; +}; + +/** @override */ +policy.Policy.prototype.getValueWidth_ = function(valueContainer) { + return valueContainer.querySelector('.value').offsetWidth + + valueContainer.querySelector('.edit-button').offsetWidth; +}; + +/** + * Start editing value. + * @private + */ +policy.Policy.prototype.onValueEditing_ = function() { + this.classList.add('value-editing-on'); + this.classList.remove('has-overflowed-value'); + this.querySelector('.value-edit-field').select(); +}; + +/** + * Update the policy to its new edited value. + * @private + */ +policy.Policy.prototype.submitEditedValue_ = function() { + var newValue = this.querySelector('.value-edit-field').value; + this.setValue_(newValue); + this.setStatus_(newValue); + this.classList.remove('value-editing-on'); + this.querySelector('.value-container').valueWidth = undefined; + this.checkOverflow(); + var showUnset = $('show-unset').checked; + this.hidden = this.unset && !showUnset || + this.name.toLowerCase().indexOf(this.parentNode.filterPattern_) == -1; + chrome.send('updateSession', [policy.Page.getInstance().getDictionary()]); + return false; +}; + +// Override policy.PolicyTable methods. + +/** + * Get policy values stored in this table. + * @returns {Object} Dictionary with policy values. + */ +policy.PolicyTable.prototype.getDictionary = function() { + var result = {}; + var policies = this.getElementsByTagName('tbody'); + for (var i = 0; i < policies.length; i++) { + var policy = policies[i]; + if (policy.unset) { + continue; + } + result[policy.name] = {value: policy.value}; + } + return result; +}; + +// Add error showing function. + +/** + * Shows an error message to the user. + * @param {String} message_name Identifier for the error message. + */ +policy.showErrorMessage = function(message_name) { + // TODO(urusant): improve error showing. + alert(loadTimeData.getString(message_name)); + console.log(loadTimeData.getString(message_name)); +}; + +// Call the main inttialization function when the page finishes loading. +document.addEventListener( + 'DOMContentLoaded', + policy.Page.getInstance().initialize.bind(policy.Page.getInstance())); diff --git a/chromium/chrome/browser/resources/print_preview/data/app_state.js b/chromium/chrome/browser/resources/print_preview/data/app_state.js index 06541447351..6e14236ee9c 100644 --- a/chromium/chrome/browser/resources/print_preview/data/app_state.js +++ b/chromium/chrome/browser/resources/print_preview/data/app_state.js @@ -26,149 +26,113 @@ print_preview.AppStateField = { VENDOR_OPTIONS: 'vendorOptions' }; + /** - * Object used to represent a recent destination in the app state. - * @constructor - * @struct + * @typedef {{id: string, + * origin: print_preview.DestinationOrigin, + * account: string, + * capabilities: ?print_preview.Cdd, + * displayName: string, + * extensionId: string, + * extensionName: string}} */ -function RecentDestination(destination) { - /** - * ID of the RecentDestination. - * @type {string} - */ - this.id = destination.id; - - /** - * Origin of the RecentDestination. - * @type {string} - */ - this.origin = destination.origin; - - /** - * Account the RecentDestination is registered for. - * @type {string} - */ - this.account = destination.account || ''; - - /** - * CDD of the RecentDestination. - * @type {print_preview.Cdd} - */ - this.capabilities = destination.capabilities; - - /** - * Name of the RecentDestination. - * @type {string} - */ - this.name = destination.name || ''; - - /** - * Extension ID associated with the RecentDestination. - * @type {string} - */ - this.extensionId = destination.extension_id || ''; +print_preview.AppStateRecentDestination; - /** - * Extension name associated with the RecentDestination. - * @type {string} - */ - this.extensionName = destination.extension_name || ''; +/** + * Creates a |AppStateRecentDestination| to represent |destination| in the app + * state. + * @param {!print_preview.Destination} destination The destination to store. + * @return {!print_preview.AppStateRecentDestination} + */ +function makeRecentDestination(destination) { + return { + id: destination.id_, + origin: destination.origin_, + account: destination.account_ || '', + capabilities: destination.capabilities, + displayName: destination.displayName_ || '', + extensionId: destination.extensionId_ || '', + extensionName: destination.extensionName_ || '', + }; } cr.define('print_preview', function() { 'use strict'; - - /** - * Object used to get and persist the print preview application state. - * @constructor - */ - function AppState() { - /** - * Internal representation of application state. - * @private {Object} - */ - this.state_ = {}; - this.state_[print_preview.AppStateField.VERSION] = AppState.VERSION_; - this.state_[print_preview.AppStateField.RECENT_DESTINATIONS] = []; - + class AppState { /** - * Whether the app state has been initialized. The app state will ignore all - * writes until it has been initialized. - * @private {boolean} + * Object used to get and persist the print preview application state. */ - this.isInitialized_ = false; + constructor() { + /** + * Internal representation of application state. + * @private {!Object} + */ + this.state_ = {}; + this.state_[print_preview.AppStateField.VERSION] = AppState.VERSION_; + this.state_[print_preview.AppStateField.RECENT_DESTINATIONS] = []; - /** - * Native Layer object to use for sending app state to C++ handler. - * @private {!print_preview.NativeLayer} - */ - this.nativeLayer_ = print_preview.NativeLayer.getInstance(); - } - - /** - * Number of recent print destinations to store across browser sessions. - * @const {number} - */ - AppState.NUM_DESTINATIONS_ = 3; + /** + * Whether the app state has been initialized. The app state will ignore + * all writes until it has been initialized. + * @private {boolean} + */ + this.isInitialized_ = false; - /** - * Current version of the app state. This value helps to understand how to - * parse earlier versions of the app state. - * @type {number} - * @const - * @private - */ - AppState.VERSION_ = 2; + /** + * Native Layer object to use for sending app state to C++ handler. + * @private {!print_preview.NativeLayer} + */ + this.nativeLayer_ = print_preview.NativeLayer.getInstance(); + } - AppState.prototype = { /** - * @return {?RecentDestination} The most recent destination, - * which is currently the selected destination. + * @return {?print_preview.AppStateRecentDestination} The most recent + * destination, which is currently the selected destination. */ get selectedDestination() { return (this.state_[print_preview.AppStateField.RECENT_DESTINATIONS] .length > 0) ? this.state_[print_preview.AppStateField.RECENT_DESTINATIONS][0] : null; - }, + } /** * @return {boolean} Whether the selected destination is valid. */ - isSelectedDestinationValid: function() { - return !!this.selectedDestination && !!this.selectedDestination.id && - !!this.selectedDestination.origin; - }, + isSelectedDestinationValid() { + var selected = this.selectedDestination; + return !!selected && !!selected.id && !!selected.origin; + } /** - * @return {?Array<!RecentDestination>} The + * @return {?Array<!print_preview.AppStateRecentDestination>} The * AppState.NUM_DESTINATIONS_ most recent destinations. */ get recentDestinations() { return this.state_[print_preview.AppStateField.RECENT_DESTINATIONS]; - }, + } /** * @param {!print_preview.AppStateField} field App state field to check if * set. * @return {boolean} Whether a field has been set in the app state. */ - hasField: function(field) { + hasField(field) { return this.state_.hasOwnProperty(field); - }, + } /** * @param {!print_preview.AppStateField} field App state field to get. * @return {?} Value of the app state field. */ - getField: function(field) { + getField(field) { if (field == print_preview.AppStateField.CUSTOM_MARGINS) { return this.state_[field] ? print_preview.Margins.parse(this.state_[field]) : null; } return this.state_[field]; - }, + } /** * Initializes the app state from a serialized string returned by the native @@ -176,12 +140,13 @@ cr.define('print_preview', function() { * @param {?string} serializedAppStateStr Serialized string representation * of the app state. */ - init: function(serializedAppStateStr) { + init(serializedAppStateStr) { if (serializedAppStateStr) { try { var state = JSON.parse(serializedAppStateStr); - if (state[print_preview.AppStateField.VERSION] == AppState.VERSION_) { - this.state_ = /** @type {Object} */ (state); + if (!!state && + state[print_preview.AppStateField.VERSION] == AppState.VERSION_) { + this.state_ = /** @type {!Object} */ (state); } } catch (e) { console.error('Unable to parse state: ' + e); @@ -209,21 +174,21 @@ cr.define('print_preview', function() { this.state_[print_preview.AppStateField.RECENT_DESTINATIONS].length = AppState.NUM_DESTINATIONS_; } - }, + } /** * Sets to initialized state. Now object will accept persist requests. */ - setInitialized: function() { + setInitialized() { this.isInitialized_ = true; - }, + } /** * Persists the given value for the given field. * @param {!print_preview.AppStateField} field Field to persist. * @param {?} value Value of field to persist. */ - persistField: function(field, value) { + persistField(field, value) { if (!this.isInitialized_) return; if (field == print_preview.AppStateField.CUSTOM_MARGINS) { @@ -232,19 +197,19 @@ cr.define('print_preview', function() { this.state_[field] = value; } this.persist_(); - }, + } /** * Persists the selected destination. * @param {!print_preview.Destination} dest Destination to persist. */ - persistSelectedDestination: function(dest) { + persistSelectedDestination(dest) { if (!this.isInitialized_) return; // Determine if this destination is already in the recent destinations, // and where in the array it is located. - var newDestination = new RecentDestination(dest); + var newDestination = makeRecentDestination(dest); var indexFound = this.state_[print_preview.AppStateField.RECENT_DESTINATIONS] .findIndex(function(recent) { @@ -275,16 +240,33 @@ cr.define('print_preview', function() { 0, 0, newDestination); this.persist_(); - }, + } /** * Calls into the native layer to persist the application state. * @private */ - persist_: function() { + persist_() { this.nativeLayer_.saveAppState(JSON.stringify(this.state_)); } - }; + } + + /** + * Number of recent print destinations to store across browser sessions. + * @const {number} + * @private + */ + AppState.NUM_DESTINATIONS_ = 3; - return {AppState: AppState}; + /** + * Current version of the app state. This value helps to understand how to + * parse earlier versions of the app state. + * @const {number} + * @private + */ + AppState.VERSION_ = 2; + + return { + AppState: AppState, + }; }); diff --git a/chromium/chrome/browser/resources/print_preview/data/capabilities_holder.js b/chromium/chrome/browser/resources/print_preview/data/capabilities_holder.js index 1201ae2d3ac..edb8094ca7e 100644 --- a/chromium/chrome/browser/resources/print_preview/data/capabilities_holder.js +++ b/chromium/chrome/browser/resources/print_preview/data/capabilities_holder.js @@ -5,33 +5,29 @@ cr.define('print_preview', function() { 'use strict'; - /** - * Mutable reference to a CDD object. - * @constructor - */ - function CapabilitiesHolder() { - /** - * Reference to the capabilities object. - * @type {?print_preview.Cdd} - * @private - */ - this.capabilities_ = null; - } + /* Mutable reference to a CDD object. */ + class CapabilitiesHolder { + constructor() { + /** + * Reference to the capabilities object. + * @private {?print_preview.Cdd} + */ + this.capabilities_ = null; + } - CapabilitiesHolder.prototype = { /** @return {?print_preview.Cdd} The instance held by the holder. */ - get: function() { + get() { return this.capabilities_; - }, + } /** * @param {!print_preview.Cdd} capabilities New instance to put into the * holder. */ - set: function(capabilities) { + set(capabilities) { this.capabilities_ = capabilities; } - }; + } // Export return {CapabilitiesHolder: CapabilitiesHolder}; diff --git a/chromium/chrome/browser/resources/print_preview/data/coordinate2d.js b/chromium/chrome/browser/resources/print_preview/data/coordinate2d.js index b336f743504..f4dff0458ad 100644 --- a/chromium/chrome/browser/resources/print_preview/data/coordinate2d.js +++ b/chromium/chrome/browser/resources/print_preview/data/coordinate2d.js @@ -5,39 +5,38 @@ cr.define('print_preview', function() { 'use strict'; - /** - * Immutable two dimensional point in space. The units of the dimensions are - * undefined. - * @param {number} x X-dimension of the point. - * @param {number} y Y-dimension of the point. - * @constructor - */ - function Coordinate2d(x, y) { + class Coordinate2d { /** - * X-dimension of the point. - * @type {number} - * @private + * Immutable two dimensional point in space. The units of the dimensions are + * undefined. + * @param {number} x X-dimension of the point. + * @param {number} y Y-dimension of the point. */ - this.x_ = x; + constructor(x, y) { + /** + * X-dimension of the point. + * @type {number} + * @private + */ + this.x_ = x; - /** - * Y-dimension of the point. - * @type {number} - * @private - */ - this.y_ = y; - } + /** + * Y-dimension of the point. + * @type {number} + * @private + */ + this.y_ = y; + } - Coordinate2d.prototype = { /** @return {number} X-dimension of the point. */ get x() { return this.x_; - }, + } /** @return {number} Y-dimension of the point. */ get y() { return this.y_; - }, + } /** * @param {number} x Amount to translate in the X dimension. @@ -45,27 +44,27 @@ cr.define('print_preview', function() { * @return {!print_preview.Coordinate2d} A new two-dimensional point * translated along the X and Y dimensions. */ - translate: function(x, y) { + translate(x, y) { return new Coordinate2d(this.x_ + x, this.y_ + y); - }, + } /** * @param {number} factor Amount to scale the X and Y dimensions. * @return {!print_preview.Coordinate2d} A new two-dimensional point scaled * by the given factor. */ - scale: function(factor) { + scale(factor) { return new Coordinate2d(this.x_ * factor, this.y_ * factor); - }, + } /** * @param {print_preview.Coordinate2d} other The point to compare against. * @return {boolean} Whether another point is equal to this one. */ - equals: function(other) { + equals(other) { return other != null && this.x_ == other.x_ && this.y_ == other.y_; } - }; + } // Export return {Coordinate2d: Coordinate2d}; diff --git a/chromium/chrome/browser/resources/print_preview/data/destination.js b/chromium/chrome/browser/resources/print_preview/data/destination.js index 24e93cb4e8a..e98026cea19 100644 --- a/chromium/chrome/browser/resources/print_preview/data/destination.js +++ b/chromium/chrome/browser/resources/print_preview/data/destination.js @@ -96,226 +96,192 @@ print_preview.Cdd; cr.define('print_preview', function() { 'use strict'; - /** - * Print destination data object that holds data for both local and cloud - * destinations. - * @param {string} id ID of the destination. - * @param {!print_preview.DestinationType} type Type of the destination. - * @param {!print_preview.DestinationOrigin} origin Origin of the - * destination. - * @param {string} displayName Display name of the destination. - * @param {boolean} isRecent Whether the destination has been used recently. - * @param {!print_preview.DestinationConnectionStatus} connectionStatus - * Connection status of the print destination. - * @param {{tags: (Array<string>|undefined), - * isOwned: (boolean|undefined), - * isEnterprisePrinter: (boolean|undefined), - * account: (string|undefined), - * lastAccessTime: (number|undefined), - * cloudID: (string|undefined), - * provisionalType: - * (print_preview.DestinationProvisionalType|undefined), - * extensionId: (string|undefined), - * extensionName: (string|undefined), - * description: (string|undefined)}=} opt_params Optional parameters - * for the destination. - * @constructor - */ - function Destination( - id, type, origin, displayName, isRecent, connectionStatus, opt_params) { - /** - * ID of the destination. - * @private {string} - */ - this.id_ = id; - - /** - * Type of the destination. - * @private {!print_preview.DestinationType} - */ - this.type_ = type; - - /** - * Origin of the destination. - * @private {!print_preview.DestinationOrigin} - */ - this.origin_ = origin; - - /** - * Display name of the destination. - * @private {string} - */ - this.displayName_ = displayName || ''; - - /** - * Whether the destination has been used recently. - * @private {boolean} - */ - this.isRecent_ = isRecent; - - /** - * Tags associated with the destination. - * @private {!Array<string>} - */ - this.tags_ = (opt_params && opt_params.tags) || []; - - /** - * Print capabilities of the destination. - * @private {?print_preview.Cdd} - */ - this.capabilities_ = null; - - /** - * Whether the destination is owned by the user. - * @private {boolean} - */ - this.isOwned_ = (opt_params && opt_params.isOwned) || false; - - /** - * Whether the destination is an enterprise policy controlled printer. - * @private {boolean} - */ - this.isEnterprisePrinter_ = - (opt_params && opt_params.isEnterprisePrinter) || false; - - /** - * Account this destination is registered for, if known. - * @private {string} - */ - this.account_ = (opt_params && opt_params.account) || ''; - - /** - * Cache of destination location fetched from tags. - * @private {?string} - */ - this.location_ = null; - - /** - * Printer description. - * @private {string} - */ - this.description_ = (opt_params && opt_params.description) || ''; - - /** - * Connection status of the destination. - * @private {!print_preview.DestinationConnectionStatus} - */ - this.connectionStatus_ = connectionStatus; - - /** - * Number of milliseconds since the epoch when the printer was last - * accessed. - * @private {number} - */ - this.lastAccessTime_ = - (opt_params && opt_params.lastAccessTime) || Date.now(); - - /** - * Cloud ID for Privet printers. - * @private {string} - */ - this.cloudID_ = (opt_params && opt_params.cloudID) || ''; - + class Destination { /** - * Extension ID for extension managed printers. - * @private {string} - */ - this.extensionId_ = (opt_params && opt_params.extensionId) || ''; - - /** - * Extension name for extension managed printers. - * @private {string} - */ - this.extensionName_ = (opt_params && opt_params.extensionName) || ''; - - /** - * Different from {@code print_preview.DestinationProvisionalType.NONE} if - * the destination is provisional. Provisional destinations cannot be - * selected as they are, but have to be resolved first (i.e. extra steps - * have to be taken to get actual destination properties, which should - * replace the provisional ones). Provisional destination resolvment flow - * will be started when the user attempts to select the destination in - * search UI. - * @private {print_preview.DestinationProvisionalType} - */ - this.provisionalType_ = (opt_params && opt_params.provisionalType) || - print_preview.DestinationProvisionalType.NONE; - - assert( - this.provisionalType_ != - print_preview.DestinationProvisionalType.NEEDS_USB_PERMISSION || - this.isExtension, - 'Provisional USB destination only supprted with extension origin.'); - } - - /** - * Prefix of the location destination tag. - * @type {!Array<string>} - * @const - */ - Destination.LOCATION_TAG_PREFIXES = - ['__cp__location=', '__cp__printer-location=']; + * Print destination data object that holds data for both local and cloud + * destinations. + * @param {string} id ID of the destination. + * @param {!print_preview.DestinationType} type Type of the destination. + * @param {!print_preview.DestinationOrigin} origin Origin of the + * destination. + * @param {string} displayName Display name of the destination. + * @param {boolean} isRecent Whether the destination has been used recently. + * @param {!print_preview.DestinationConnectionStatus} connectionStatus + * Connection status of the print destination. + * @param {{tags: (Array<string>|undefined), + * isOwned: (boolean|undefined), + * isEnterprisePrinter: (boolean|undefined), + * account: (string|undefined), + * lastAccessTime: (number|undefined), + * cloudID: (string|undefined), + * provisionalType: + * (print_preview.DestinationProvisionalType|undefined), + * extensionId: (string|undefined), + * extensionName: (string|undefined), + * description: (string|undefined)}=} opt_params Optional + * parameters for the destination. + */ + constructor( + id, type, origin, displayName, isRecent, connectionStatus, opt_params) { + /** + * ID of the destination. + * @private {string} + */ + this.id_ = id; + + /** + * Type of the destination. + * @private {!print_preview.DestinationType} + */ + this.type_ = type; + + /** + * Origin of the destination. + * @private {!print_preview.DestinationOrigin} + */ + this.origin_ = origin; + + /** + * Display name of the destination. + * @private {string} + */ + this.displayName_ = displayName || ''; + + /** + * Whether the destination has been used recently. + * @private {boolean} + */ + this.isRecent_ = isRecent; - /** - * Enumeration of Google-promoted destination IDs. - * @enum {string} - */ - Destination.GooglePromotedId = { - DOCS: '__google__docs', - SAVE_AS_PDF: 'Save as PDF' - }; + /** + * Tags associated with the destination. + * @private {!Array<string>} + */ + this.tags_ = (opt_params && opt_params.tags) || []; + + /** + * Print capabilities of the destination. + * @private {?print_preview.Cdd} + */ + this.capabilities_ = null; + + /** + * Whether the destination is owned by the user. + * @private {boolean} + */ + this.isOwned_ = (opt_params && opt_params.isOwned) || false; + + /** + * Whether the destination is an enterprise policy controlled printer. + * @private {boolean} + */ + this.isEnterprisePrinter_ = + (opt_params && opt_params.isEnterprisePrinter) || false; + + /** + * Account this destination is registered for, if known. + * @private {string} + */ + this.account_ = (opt_params && opt_params.account) || ''; + + /** + * Cache of destination location fetched from tags. + * @private {?string} + */ + this.location_ = null; + + /** + * Printer description. + * @private {string} + */ + this.description_ = (opt_params && opt_params.description) || ''; + + /** + * Connection status of the destination. + * @private {!print_preview.DestinationConnectionStatus} + */ + this.connectionStatus_ = connectionStatus; + + /** + * Number of milliseconds since the epoch when the printer was last + * accessed. + * @private {number} + */ + this.lastAccessTime_ = + (opt_params && opt_params.lastAccessTime) || Date.now(); + + /** + * Cloud ID for Privet printers. + * @private {string} + */ + this.cloudID_ = (opt_params && opt_params.cloudID) || ''; + + /** + * Extension ID for extension managed printers. + * @private {string} + */ + this.extensionId_ = (opt_params && opt_params.extensionId) || ''; + + /** + * Extension name for extension managed printers. + * @private {string} + */ + this.extensionName_ = (opt_params && opt_params.extensionName) || ''; + + /** + * Different from {@code print_preview.DestinationProvisionalType.NONE} if + * the destination is provisional. Provisional destinations cannot be + * selected as they are, but have to be resolved first (i.e. extra steps + * have to be taken to get actual destination properties, which should + * replace the provisional ones). Provisional destination resolvment flow + * will be started when the user attempts to select the destination in + * search UI. + * @private {print_preview.DestinationProvisionalType} + */ + this.provisionalType_ = (opt_params && opt_params.provisionalType) || + print_preview.DestinationProvisionalType.NONE; - /** - * Enumeration of relative icon URLs for various types of destinations. - * @enum {string} - * @private - */ - Destination.IconUrl_ = { - CLOUD: 'images/printer.png', - CLOUD_SHARED: 'images/printer_shared.png', - LOCAL: 'images/printer.png', - MOBILE: 'images/mobile.png', - MOBILE_SHARED: 'images/mobile_shared.png', - THIRD_PARTY: 'images/third_party.png', - PDF: 'images/pdf.png', - DOCS: 'images/google_doc.png', - ENTERPRISE: 'images/business.svg' - }; + assert( + this.provisionalType_ != + print_preview.DestinationProvisionalType + .NEEDS_USB_PERMISSION || + this.isExtension, + 'Provisional USB destination only supprted with extension origin.'); + } - Destination.prototype = { /** @return {string} ID of the destination. */ get id() { return this.id_; - }, + } /** @return {!print_preview.DestinationType} Type of the destination. */ get type() { return this.type_; - }, + } /** * @return {!print_preview.DestinationOrigin} Origin of the destination. */ get origin() { return this.origin_; - }, + } /** @return {string} Display name of the destination. */ get displayName() { return this.displayName_; - }, + } /** @return {boolean} Whether the destination has been used recently. */ get isRecent() { return this.isRecent_; - }, + } /** * @param {boolean} isRecent Whether the destination has been used recently. */ set isRecent(isRecent) { this.isRecent_ = isRecent; - }, + } /** * @return {boolean} Whether the user owns the destination. Only applies to @@ -323,14 +289,14 @@ cr.define('print_preview', function() { */ get isOwned() { return this.isOwned_; - }, + } /** * @return {string} Account this destination is registered for, if known. */ get account() { return this.account_; - }, + } /** @return {boolean} Whether the destination is local or cloud-based. */ get isLocal() { @@ -340,12 +306,12 @@ cr.define('print_preview', function() { (this.origin_ == print_preview.DestinationOrigin.PRIVET && this.connectionStatus_ != print_preview.DestinationConnectionStatus.UNREGISTERED); - }, + } /** @return {boolean} Whether the destination is a Privet local printer */ get isPrivet() { return this.origin_ == print_preview.DestinationOrigin.PRIVET; - }, + } /** * @return {boolean} Whether the destination is an extension managed @@ -353,7 +319,7 @@ cr.define('print_preview', function() { */ get isExtension() { return this.origin_ == print_preview.DestinationOrigin.EXTENSION; - }, + } /** * @return {string} The location of the destination, or an empty string if @@ -362,17 +328,17 @@ cr.define('print_preview', function() { get location() { if (this.location_ == null) { this.location_ = ''; - this.tags_.some(function(tag) { - return Destination.LOCATION_TAG_PREFIXES.some(function(prefix) { + this.tags_.some(tag => { + return Destination.LOCATION_TAG_PREFIXES.some(prefix => { if (tag.startsWith(prefix)) { this.location_ = tag.substring(prefix.length) || ''; return true; } - }, this); - }, this); + }); + }); } return this.location_; - }, + } /** * @return {string} The description of the destination, or an empty string, @@ -380,7 +346,7 @@ cr.define('print_preview', function() { */ get description() { return this.description_; - }, + } /** * @return {string} Most relevant string to help user to identify this @@ -391,17 +357,17 @@ cr.define('print_preview', function() { return this.account_; } return this.location || this.extensionName || this.description; - }, + } /** @return {!Array<string>} Tags associated with the destination. */ get tags() { return this.tags_.slice(0); - }, + } /** @return {string} Cloud ID associated with the destination */ get cloudID() { return this.cloudID_; - }, + } /** * @return {string} Extension ID associated with the destination. Non-empty @@ -409,7 +375,7 @@ cr.define('print_preview', function() { */ get extensionId() { return this.extensionId_; - }, + } /** * @return {string} Extension name associated with the destination. @@ -417,20 +383,21 @@ cr.define('print_preview', function() { */ get extensionName() { return this.extensionName_; - }, + } /** @return {?print_preview.Cdd} Print capabilities of the destination. */ get capabilities() { return this.capabilities_; - }, + } /** - * @param {!print_preview.Cdd} capabilities Print capabilities of the + * @param {?print_preview.Cdd} capabilities Print capabilities of the * destination. */ set capabilities(capabilities) { - this.capabilities_ = capabilities; - }, + if (capabilities) + this.capabilities_ = capabilities; + } /** * @return {!print_preview.DestinationConnectionStatus} Connection status @@ -438,7 +405,7 @@ cr.define('print_preview', function() { */ get connectionStatus() { return this.connectionStatus_; - }, + } /** * @param {!print_preview.DestinationConnectionStatus} status Connection @@ -446,7 +413,7 @@ cr.define('print_preview', function() { */ set connectionStatus(status) { this.connectionStatus_ = status; - }, + } /** @return {boolean} Whether the destination is considered offline. */ get isOffline() { @@ -456,7 +423,7 @@ cr.define('print_preview', function() { print_preview.DestinationConnectionStatus.DORMANT ], this.connectionStatus_); - }, + } /** @return {string} Human readable status for offline destination. */ get offlineStatusText() { @@ -475,7 +442,7 @@ cr.define('print_preview', function() { offlineMessageId = 'offline'; } return loadTimeData.getString(offlineMessageId); - }, + } /** * @return {number} Number of milliseconds since the epoch when the printer @@ -483,7 +450,7 @@ cr.define('print_preview', function() { */ get lastAccessTime() { return this.lastAccessTime_; - }, + } /** @return {string} Relative URL of the destination's icon. */ get iconUrl() { @@ -497,7 +464,7 @@ cr.define('print_preview', function() { return Destination.IconUrl_.ENTERPRISE; } if (this.isLocal) { - return Destination.IconUrl_.LOCAL; + return Destination.IconUrl_.LOCAL_1X; } if (this.type_ == print_preview.DestinationType.MOBILE && this.isOwned_) { return Destination.IconUrl_.MOBILE; @@ -506,10 +473,30 @@ cr.define('print_preview', function() { return Destination.IconUrl_.MOBILE_SHARED; } if (this.isOwned_) { - return Destination.IconUrl_.CLOUD; + return Destination.IconUrl_.CLOUD_1X; + } + return Destination.IconUrl_.CLOUD_SHARED_1X; + } + + /** + * @return {string} The srcset="" attribute of a destination. Generally used + * for a 2x (e.g. HiDPI) icon. Can be empty or of the format '<url> 2x'. + */ + get srcSet() { + let srcSetIcon = ''; + let iconUrl = this.iconUrl; + if (iconUrl == Destination.IconUrl_.LOCAL_1X) { + srcSetIcon = Destination.IconUrl_.LOCAL_2X; + } else if (iconUrl == Destination.IconUrl_.CLOUD_1X) { + srcSetIcon = Destination.IconUrl_.CLOUD_2X; + } else if (iconUrl == Destination.IconUrl_.CLOUD_SHARED_1X) { + srcSetIcon = Destination.IconUrl_.CLOUD_SHARED_2X; } - return Destination.IconUrl_.CLOUD_SHARED; - }, + if (srcSetIcon) { + srcSetIcon += ' 2x'; + } + return srcSetIcon; + } /** * @return {!Array<string>} Properties (besides display name) to match @@ -517,7 +504,7 @@ cr.define('print_preview', function() { */ get extraPropertiesToMatch() { return [this.location, this.description]; - }, + } /** * Matches a query against the destination. @@ -525,13 +512,11 @@ cr.define('print_preview', function() { * @return {boolean} {@code true} if the query matches this destination, * {@code false} otherwise. */ - matches: function(query) { + matches(query) { return !!this.displayName_.match(query) || !!this.extensionName_.match(query) || - this.extraPropertiesToMatch.some(function(property) { - return property.match(query); - }); - }, + this.extraPropertiesToMatch.some(p => p.match(query)); + } /** * Gets the destination's provisional type. @@ -539,7 +524,7 @@ cr.define('print_preview', function() { */ get provisionalType() { return this.provisionalType_; - }, + } /** * Whether the destinaion is provisional. @@ -548,7 +533,7 @@ cr.define('print_preview', function() { get isProvisional() { return this.provisionalType_ != print_preview.DestinationProvisionalType.NONE; - }, + } /** * Whether the printer is enterprise policy controlled printer. @@ -556,7 +541,44 @@ cr.define('print_preview', function() { */ get isEnterprisePrinter() { return this.isEnterprisePrinter_; - }, + } + } + + /** + * Prefix of the location destination tag. + * @type {!Array<string>} + * @const + */ + Destination.LOCATION_TAG_PREFIXES = + ['__cp__location=', '__cp__printer-location=']; + + /** + * Enumeration of Google-promoted destination IDs. + * @enum {string} + */ + Destination.GooglePromotedId = { + DOCS: '__google__docs', + SAVE_AS_PDF: 'Save as PDF' + }; + + /** + * Enumeration of relative icon URLs for various types of destinations. + * @enum {string} + * @private + */ + Destination.IconUrl_ = { + CLOUD_1X: 'images/1x/printer.png', + CLOUD_2X: 'images/2x/printer.png', + CLOUD_SHARED_1X: 'images/1x/printer_shared.png', + CLOUD_SHARED_2X: 'images/2x/printer_shared.png', + LOCAL_1X: 'images/1x/printer.png', + LOCAL_2X: 'images/2x/printer.png', + MOBILE: 'images/mobile.png', + MOBILE_SHARED: 'images/mobile_shared.png', + THIRD_PARTY: 'images/third_party.png', + PDF: 'images/pdf.png', + DOCS: 'images/google_doc.png', + ENTERPRISE: 'images/business.svg' }; // Export diff --git a/chromium/chrome/browser/resources/print_preview/data/destination_match.js b/chromium/chrome/browser/resources/print_preview/data/destination_match.js index 3152abe2342..165aa22f013 100644 --- a/chromium/chrome/browser/resources/print_preview/data/destination_match.js +++ b/chromium/chrome/browser/resources/print_preview/data/destination_match.js @@ -4,59 +4,74 @@ cr.define('print_preview', function() { 'use strict'; - /** - * A set of key parameters describing a destination used to determine - * if two destinations are the same. - * @param {!Array<!print_preview.DestinationOrigin>} origins Match - * destinations from these origins. - * @param {RegExp} idRegExp Match destination's id. - * @param {RegExp} displayNameRegExp Match destination's displayName. - * @param {boolean} skipVirtualDestinations Whether to ignore virtual - * destinations, for example, Save as PDF. - * @constructor + * Converts DestinationOrigin to PrinterType. + * @param {!print_preview.DestinationOrigin} origin The printer's + * destination origin. + * return {?print_preview.PrinterType} The corresponding PrinterType. + * Returns null if no match is found. */ - function DestinationMatch( - origins, idRegExp, displayNameRegExp, skipVirtualDestinations) { - /** @private {!Array<!print_preview.DestinationOrigin>} */ - this.origins_ = origins; + var originToType = function(origin) { + if (origin === print_preview.DestinationOrigin.LOCAL || + origin === print_preview.DestinationOrigin.CROS) { + return print_preview.PrinterType.LOCAL_PRINTER; + } + if (origin === print_preview.DestinationOrigin.PRIVET) + return print_preview.PrinterType.PRIVET_PRINTER; + if (origin === print_preview.DestinationOrigin.EXTENSION) + return print_preview.PrinterType.EXTENSION_PRINTER; + return null; + }; - /** @private {RegExp} */ - this.idRegExp_ = idRegExp; + class DestinationMatch { + /** + * A set of key parameters describing a destination used to determine + * if two destinations are the same. + * @param {!Array<!print_preview.DestinationOrigin>} origins Match + * destinations from these origins. + * @param {RegExp} idRegExp Match destination's id. + * @param {RegExp} displayNameRegExp Match destination's displayName. + * @param {boolean} skipVirtualDestinations Whether to ignore virtual + * destinations, for example, Save as PDF. + */ + constructor(origins, idRegExp, displayNameRegExp, skipVirtualDestinations) { + /** @private {!Array<!print_preview.DestinationOrigin>} */ + this.origins_ = origins; - /** @private {RegExp} */ - this.displayNameRegExp_ = displayNameRegExp; + /** @private {RegExp} */ + this.idRegExp_ = idRegExp; - /** @private {boolean} */ - this.skipVirtualDestinations_ = skipVirtualDestinations; - } + /** @private {RegExp} */ + this.displayNameRegExp_ = displayNameRegExp; - DestinationMatch.prototype = { + /** @private {boolean} */ + this.skipVirtualDestinations_ = skipVirtualDestinations; + } /** * @param {string} origin Origin to match. * @return {boolean} Whether the origin is one of the {@code origins_}. */ - matchOrigin: function(origin) { + matchOrigin(origin) { return arrayContains(this.origins_, origin); - }, + } /** * @param {string} id Id of the destination. * @param {string} origin Origin of the destination. * @return {boolean} Whether destination is the same as initial. */ - matchIdAndOrigin: function(id, origin) { + matchIdAndOrigin(id, origin) { return this.matchOrigin(origin) && !!this.idRegExp_ && this.idRegExp_.test(id); - }, + } /** * @param {!print_preview.Destination} destination Destination to match. * @return {boolean} Whether {@code destination} matches the last user * selected one. */ - match: function(destination) { + match(destination) { if (!this.matchOrigin(destination.origin)) { return false; } @@ -72,7 +87,7 @@ cr.define('print_preview', function() { return false; } return true; - }, + } /** * @param {!print_preview.Destination} destination Destination to check. @@ -80,7 +95,7 @@ cr.define('print_preview', function() { * destination selection. * @private */ - isVirtualDestination_: function(destination) { + isVirtualDestination_(destination) { if (destination.origin == print_preview.DestinationOrigin.LOCAL) { return arrayContains( [print_preview.Destination.GooglePromotedId.SAVE_AS_PDF], @@ -89,8 +104,16 @@ cr.define('print_preview', function() { return arrayContains( [print_preview.Destination.GooglePromotedId.DOCS], destination.id); } - }; + + /** + * @return {?print_preview.PrinterType} The printer type of this + * destination match. Will return null for Cloud destinations. + */ + getType() { + return originToType(this.origins_[0]); + } + } // Export - return {DestinationMatch: DestinationMatch}; + return {originToType: originToType, DestinationMatch: DestinationMatch}; }); diff --git a/chromium/chrome/browser/resources/print_preview/data/destination_store.js b/chromium/chrome/browser/resources/print_preview/data/destination_store.js index 8ef5101ccb1..80072b56887 100644 --- a/chromium/chrome/browser/resources/print_preview/data/destination_store.js +++ b/chromium/chrome/browser/resources/print_preview/data/destination_store.js @@ -2,404 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +cr.exportPath('print_preview'); + +/** + * Printer search statuses used by the destination store. + * @enum {string} + */ +print_preview.DestinationStorePrinterSearchStatus = { + START: 'start', + SEARCHING: 'searching', + DONE: 'done' +}; + cr.define('print_preview', function() { 'use strict'; - - /** - * A data store that stores destinations and dispatches events when the data - * store changes. - * @param {!print_preview.UserInfo} userInfo User information repository. - * @param {!print_preview.AppState} appState Application state. - * @param {!WebUIListenerTracker} listenerTracker Tracker for WebUI listeners - * added in DestinationStore constructor. - * @constructor - * @extends {cr.EventTarget} - */ - function DestinationStore(userInfo, appState, listenerTracker) { - cr.EventTarget.call(this); - - /** - * Used to fetch local print destinations. - * @private {!print_preview.NativeLayer} - */ - this.nativeLayer_ = print_preview.NativeLayer.getInstance(); - - /** - * User information repository. - * @private {!print_preview.UserInfo} - */ - this.userInfo_ = userInfo; - - /** - * Used to load and persist the selected destination. - * @private {!print_preview.AppState} - */ - this.appState_ = appState; - - /** - * Used to track metrics. - * @private {!print_preview.DestinationSearchMetricsContext} - */ - this.metrics_ = new print_preview.DestinationSearchMetricsContext(); - - /** - * Internal backing store for the data store. - * @private {!Array<!print_preview.Destination>} - */ - this.destinations_ = []; - - /** - * Cache used for constant lookup of destinations by origin and id. - * @private {Object<!print_preview.Destination>} - */ - this.destinationMap_ = {}; - - /** - * Currently selected destination. - * @private {print_preview.Destination} - */ - this.selectedDestination_ = null; - - /** - * Whether the destination store will auto select the destination that - * matches this set of parameters. - * @private {print_preview.DestinationMatch} - */ - this.autoSelectMatchingDestination_ = null; - - /** - * Event tracker used to track event listeners of the destination store. - * @private {!EventTracker} - */ - this.tracker_ = new EventTracker(); - - /** - * Whether PDF printer is enabled. It's disabled, for example, in App Kiosk - * mode. - * @private {boolean} - */ - this.pdfPrinterEnabled_ = false; - - /** - * ID of the system default destination. - * @private {?string} - */ - this.systemDefaultDestinationId_ = null; - - /** - * Used to fetch cloud-based print destinations. - * @private {cloudprint.CloudPrintInterface} - */ - this.cloudPrintInterface_ = null; - - /** - * Maps user account to the list of origins for which destinations are - * already loaded. - * @private {!Object<Array<!print_preview.DestinationOrigin>>} - */ - this.loadedCloudOrigins_ = {}; - - /** - * ID of a timeout after the initial destination ID is set. If no inserted - * destination matches the initial destination ID after the specified - * timeout, the first destination in the store will be automatically - * selected. - * @private {?number} - */ - this.autoSelectTimeout_ = null; - - /** - * Whether a search for local destinations is in progress. - * @private {boolean} - */ - this.isLocalDestinationSearchInProgress_ = false; - - /** - * Whether the destination store has already loaded or is loading all local - * destinations. - * @private {boolean} - */ - this.hasLoadedAllLocalDestinations_ = false; - - /** - * Whether a search for privet destinations is in progress. - * @private {boolean} - */ - this.isPrivetDestinationSearchInProgress_ = false; - - /** - * Whether the destination store has already loaded or is loading all privet - * destinations. - * @private {boolean} - */ - this.hasLoadedAllPrivetDestinations_ = false; - - /** - * Whether a search for extension destinations is in progress. - * @private {boolean} - */ - this.isExtensionDestinationSearchInProgress_ = false; - - /** - * Whether the destination store has already loaded all extension - * destinations. - * @private {boolean} - */ - this.hasLoadedAllExtensionDestinations_ = false; - - /** - * ID of a timeout set at the start of an extension destination search. The - * timeout ends the search. - * @private {?number} - */ - this.extensionSearchTimeout_ = null; - - /** - * MDNS service name of destination that we are waiting to register. - * @private {?string} - */ - this.waitForRegisterDestination_ = null; - - /** - * Local destinations are CROS destinations on ChromeOS because they require - * extra setup. - * @private {!print_preview.DestinationOrigin} - */ - this.platformOrigin_ = cr.isChromeOS ? - print_preview.DestinationOrigin.CROS : - print_preview.DestinationOrigin.LOCAL; - - /** - * Whether to default to the system default printer instead of the most - * recent destination. - * @private {boolean} - */ - this.useSystemDefaultAsDefault_ = - loadTimeData.getBoolean('useSystemDefaultPrinter'); - - this.reset_(); - - this.addWebUIEventListeners_(listenerTracker); - } - - /** - * Event types dispatched by the data store. - * @enum {string} - */ - DestinationStore.EventType = { - DESTINATION_SEARCH_DONE: - 'print_preview.DestinationStore.DESTINATION_SEARCH_DONE', - DESTINATION_SEARCH_STARTED: - 'print_preview.DestinationStore.DESTINATION_SEARCH_STARTED', - DESTINATION_SELECT: 'print_preview.DestinationStore.DESTINATION_SELECT', - DESTINATIONS_INSERTED: - 'print_preview.DestinationStore.DESTINATIONS_INSERTED', - PROVISIONAL_DESTINATION_RESOLVED: - 'print_preview.DestinationStore.PROVISIONAL_DESTINATION_RESOLVED', - CACHED_SELECTED_DESTINATION_INFO_READY: - 'print_preview.DestinationStore.CACHED_SELECTED_DESTINATION_INFO_READY', - SELECTED_DESTINATION_CAPABILITIES_READY: 'print_preview.DestinationStore' + - '.SELECTED_DESTINATION_CAPABILITIES_READY', - }; - - /** - * Delay in milliseconds before the destination store ignores the initial - * destination ID and just selects any printer (since the initial destination - * was not found). - * @private {number} - * @const - */ - DestinationStore.AUTO_SELECT_TIMEOUT_ = 15000; - - /** - * Maximum amount of time spent searching for extension destinations, in - * milliseconds. - * @private {number} - * @const - */ - DestinationStore.EXTENSION_SEARCH_DURATION_ = 5000; - - /** - * Human readable names for media sizes in the cloud print CDD. - * https://developers.google.com/cloud-print/docs/cdd - * @private {Object<string>} - * @const - */ - DestinationStore.MEDIA_DISPLAY_NAMES_ = { - 'ISO_2A0': '2A0', - 'ISO_A0': 'A0', - 'ISO_A0X3': 'A0x3', - 'ISO_A1': 'A1', - 'ISO_A10': 'A10', - 'ISO_A1X3': 'A1x3', - 'ISO_A1X4': 'A1x4', - 'ISO_A2': 'A2', - 'ISO_A2X3': 'A2x3', - 'ISO_A2X4': 'A2x4', - 'ISO_A2X5': 'A2x5', - 'ISO_A3': 'A3', - 'ISO_A3X3': 'A3x3', - 'ISO_A3X4': 'A3x4', - 'ISO_A3X5': 'A3x5', - 'ISO_A3X6': 'A3x6', - 'ISO_A3X7': 'A3x7', - 'ISO_A3_EXTRA': 'A3 Extra', - 'ISO_A4': 'A4', - 'ISO_A4X3': 'A4x3', - 'ISO_A4X4': 'A4x4', - 'ISO_A4X5': 'A4x5', - 'ISO_A4X6': 'A4x6', - 'ISO_A4X7': 'A4x7', - 'ISO_A4X8': 'A4x8', - 'ISO_A4X9': 'A4x9', - 'ISO_A4_EXTRA': 'A4 Extra', - 'ISO_A4_TAB': 'A4 Tab', - 'ISO_A5': 'A5', - 'ISO_A5_EXTRA': 'A5 Extra', - 'ISO_A6': 'A6', - 'ISO_A7': 'A7', - 'ISO_A8': 'A8', - 'ISO_A9': 'A9', - 'ISO_B0': 'B0', - 'ISO_B1': 'B1', - 'ISO_B10': 'B10', - 'ISO_B2': 'B2', - 'ISO_B3': 'B3', - 'ISO_B4': 'B4', - 'ISO_B5': 'B5', - 'ISO_B5_EXTRA': 'B5 Extra', - 'ISO_B6': 'B6', - 'ISO_B6C4': 'B6C4', - 'ISO_B7': 'B7', - 'ISO_B8': 'B8', - 'ISO_B9': 'B9', - 'ISO_C0': 'C0', - 'ISO_C1': 'C1', - 'ISO_C10': 'C10', - 'ISO_C2': 'C2', - 'ISO_C3': 'C3', - 'ISO_C4': 'C4', - 'ISO_C5': 'C5', - 'ISO_C6': 'C6', - 'ISO_C6C5': 'C6C5', - 'ISO_C7': 'C7', - 'ISO_C7C6': 'C7C6', - 'ISO_C8': 'C8', - 'ISO_C9': 'C9', - 'ISO_DL': 'Envelope DL', - 'ISO_RA0': 'RA0', - 'ISO_RA1': 'RA1', - 'ISO_RA2': 'RA2', - 'ISO_SRA0': 'SRA0', - 'ISO_SRA1': 'SRA1', - 'ISO_SRA2': 'SRA2', - 'JIS_B0': 'B0 (JIS)', - 'JIS_B1': 'B1 (JIS)', - 'JIS_B10': 'B10 (JIS)', - 'JIS_B2': 'B2 (JIS)', - 'JIS_B3': 'B3 (JIS)', - 'JIS_B4': 'B4 (JIS)', - 'JIS_B5': 'B5 (JIS)', - 'JIS_B6': 'B6 (JIS)', - 'JIS_B7': 'B7 (JIS)', - 'JIS_B8': 'B8 (JIS)', - 'JIS_B9': 'B9 (JIS)', - 'JIS_EXEC': 'Executive (JIS)', - 'JPN_CHOU2': 'Choukei 2', - 'JPN_CHOU3': 'Choukei 3', - 'JPN_CHOU4': 'Choukei 4', - 'JPN_HAGAKI': 'Hagaki', - 'JPN_KAHU': 'Kahu Envelope', - 'JPN_KAKU2': 'Kaku 2', - 'JPN_OUFUKU': 'Oufuku Hagaki', - 'JPN_YOU4': 'You 4', - 'NA_10X11': '10x11', - 'NA_10X13': '10x13', - 'NA_10X14': '10x14', - 'NA_10X15': '10x15', - 'NA_11X12': '11x12', - 'NA_11X15': '11x15', - 'NA_12X19': '12x19', - 'NA_5X7': '5x7', - 'NA_6X9': '6x9', - 'NA_7X9': '7x9', - 'NA_9X11': '9x11', - 'NA_A2': 'A2', - 'NA_ARCH_A': 'Arch A', - 'NA_ARCH_B': 'Arch B', - 'NA_ARCH_C': 'Arch C', - 'NA_ARCH_D': 'Arch D', - 'NA_ARCH_E': 'Arch E', - 'NA_ASME_F': 'ASME F', - 'NA_B_PLUS': 'B-plus', - 'NA_C': 'C', - 'NA_C5': 'C5', - 'NA_D': 'D', - 'NA_E': 'E', - 'NA_EDP': 'EDP', - 'NA_EUR_EDP': 'European EDP', - 'NA_EXECUTIVE': 'Executive', - 'NA_F': 'F', - 'NA_FANFOLD_EUR': 'FanFold European', - 'NA_FANFOLD_US': 'FanFold US', - 'NA_FOOLSCAP': 'FanFold German Legal', - 'NA_GOVT_LEGAL': 'Government Legal', - 'NA_GOVT_LETTER': 'Government Letter', - 'NA_INDEX_3X5': 'Index 3x5', - 'NA_INDEX_4X6': 'Index 4x6', - 'NA_INDEX_4X6_EXT': 'Index 4x6 ext', - 'NA_INDEX_5X8': '5x8', - 'NA_INVOICE': 'Invoice', - 'NA_LEDGER': 'Tabloid', // Ledger in portrait is called Tabloid. - 'NA_LEGAL': 'Legal', - 'NA_LEGAL_EXTRA': 'Legal extra', - 'NA_LETTER': 'Letter', - 'NA_LETTER_EXTRA': 'Letter extra', - 'NA_LETTER_PLUS': 'Letter plus', - 'NA_MONARCH': 'Monarch', - 'NA_NUMBER_10': 'Envelope #10', - 'NA_NUMBER_11': 'Envelope #11', - 'NA_NUMBER_12': 'Envelope #12', - 'NA_NUMBER_14': 'Envelope #14', - 'NA_NUMBER_9': 'Envelope #9', - 'NA_PERSONAL': 'Personal', - 'NA_QUARTO': 'Quarto', - 'NA_SUPER_A': 'Super A', - 'NA_SUPER_B': 'Super B', - 'NA_WIDE_FORMAT': 'Wide format', - 'OM_DAI_PA_KAI': 'Dai-pa-kai', - 'OM_FOLIO': 'Folio', - 'OM_FOLIO_SP': 'Folio SP', - 'OM_INVITE': 'Invite Envelope', - 'OM_ITALIAN': 'Italian Envelope', - 'OM_JUURO_KU_KAI': 'Juuro-ku-kai', - 'OM_LARGE_PHOTO': 'Large photo', - 'OM_OFICIO': 'Oficio', - 'OM_PA_KAI': 'Pa-kai', - 'OM_POSTFIX': 'Postfix Envelope', - 'OM_SMALL_PHOTO': 'Small photo', - 'PRC_1': 'prc1 Envelope', - 'PRC_10': 'prc10 Envelope', - 'PRC_16K': 'prc 16k', - 'PRC_2': 'prc2 Envelope', - 'PRC_3': 'prc3 Envelope', - 'PRC_32K': 'prc 32k', - 'PRC_4': 'prc4 Envelope', - 'PRC_5': 'prc5 Envelope', - 'PRC_6': 'prc6 Envelope', - 'PRC_7': 'prc7 Envelope', - 'PRC_8': 'prc8 Envelope', - 'ROC_16K': 'ROC 16K', - 'ROC_8K': 'ROC 8k', - }; - /** * Localizes printer capabilities. - * @param {!print_preview.Cdd} capabilities Printer capabilities to localize. + * @param {!print_preview.Cdd} capabilities Printer capabilities to + * localize. * @return {!print_preview.Cdd} Localized capabilities. - * @private */ - DestinationStore.localizeCapabilities_ = function(capabilities) { + var localizeCapabilities = function(capabilities) { if (!capabilities.printer) return capabilities; @@ -422,9 +45,8 @@ cr.define('print_preview', function() { * @param {!Object} a Media to compare. * @param {!Object} b Media to compare. * @return {number} 1 if a > b, -1 if a < b, or 0 if a == b. - * @private */ - DestinationStore.compareMediaNames_ = function(a, b) { + var compareMediaNames = function(a, b) { var nameA = a.custom_display_name_localized || a.custom_display_name; var nameB = b.custom_display_name_localized || b.custom_display_name; return nameA == nameB ? 0 : (nameA > nameB ? 1 : -1); @@ -432,11 +54,12 @@ cr.define('print_preview', function() { /** * Sort printer media sizes. - * @param {!print_preview.Cdd} capabilities Printer capabilities to localize. + * @param {!print_preview.Cdd} capabilities Printer capabilities to + * localize. * @return {!print_preview.Cdd} Localized capabilities. * @private */ - DestinationStore.sortMediaSizes_ = function(capabilities) { + var sortMediaSizes = function(capabilities) { if (!capabilities.printer) return capabilities; @@ -482,12 +105,12 @@ cr.define('print_preview', function() { } // For each category, sort by name. - categoryStandardNA.sort(DestinationStore.compareMediaNames_); - categoryStandardCN.sort(DestinationStore.compareMediaNames_); - categoryStandardISO.sort(DestinationStore.compareMediaNames_); - categoryStandardJP.sort(DestinationStore.compareMediaNames_); - categoryStandardMisc.sort(DestinationStore.compareMediaNames_); - categoryCustom.sort(DestinationStore.compareMediaNames_); + categoryStandardNA.sort(compareMediaNames); + categoryStandardCN.sort(compareMediaNames); + categoryStandardISO.sort(compareMediaNames); + categoryStandardJP.sort(compareMediaNames); + categoryStandardMisc.sort(compareMediaNames); + categoryCustom.sort(compareMediaNames); // Then put it all back together. mediaSize.option = categoryStandardNA; @@ -497,8 +120,157 @@ cr.define('print_preview', function() { return capabilities; }; - DestinationStore.prototype = { - __proto__: cr.EventTarget.prototype, + + class DestinationStore extends cr.EventTarget { + /** + * A data store that stores destinations and dispatches events when the + * data store changes. + * @param {!print_preview.UserInfo} userInfo User information repository. + * @param {!print_preview.AppState} appState Application state. + * @param {!WebUIListenerTracker} listenerTracker Tracker for WebUI + * listeners added in DestinationStore constructor. + */ + constructor(userInfo, appState, listenerTracker) { + super(); + + /** + * Used to fetch local print destinations. + * @private {!print_preview.NativeLayer} + */ + this.nativeLayer_ = print_preview.NativeLayer.getInstance(); + + /** + * User information repository. + * @private {!print_preview.UserInfo} + */ + this.userInfo_ = userInfo; + + /** + * Used to load and persist the selected destination. + * @private {!print_preview.AppState} + */ + this.appState_ = appState; + + /** + * Used to track metrics. + * @private {!print_preview.DestinationSearchMetricsContext} + */ + this.metrics_ = new print_preview.DestinationSearchMetricsContext(); + + /** + * Internal backing store for the data store. + * @private {!Array<!print_preview.Destination>} + */ + this.destinations_ = []; + + /** + * Cache used for constant lookup of destinations by origin and id. + * @private {Object<!print_preview.Destination>} + */ + this.destinationMap_ = {}; + + /** + * Currently selected destination. + * @private {print_preview.Destination} + */ + this.selectedDestination_ = null; + + /** + * Whether the destination store will auto select the destination that + * matches this set of parameters. + * @private {print_preview.DestinationMatch} + */ + this.autoSelectMatchingDestination_ = null; + + /** + * Event tracker used to track event listeners of the destination store. + * @private {!EventTracker} + */ + this.tracker_ = new EventTracker(); + + /** + * Whether PDF printer is enabled. It's disabled, for example, in App + * Kiosk mode. + * @private {boolean} + */ + this.pdfPrinterEnabled_ = false; + + /** + * ID of the system default destination. + * @private {string} + */ + this.systemDefaultDestinationId_ = ''; + + /** + * Used to fetch cloud-based print destinations. + * @private {cloudprint.CloudPrintInterface} + */ + this.cloudPrintInterface_ = null; + + /** + * Maps user account to the list of origins for which destinations are + * already loaded. + * @private {!Object<Array<!print_preview.DestinationOrigin>>} + */ + this.loadedCloudOrigins_ = {}; + + /** + * ID of a timeout after the initial destination ID is set. If no inserted + * destination matches the initial destination ID after the specified + * timeout, the first destination in the store will be automatically + * selected. + * @private {?number} + */ + this.autoSelectTimeout_ = null; + + /** + * Whether a search for destinations is in progress for each type of + * printer. + * @private {!Map<!print_preview.PrinterType, + * !print_preview.DestinationStorePrinterSearchStatus>} + */ + this.destinationSearchStatus_ = new Map([ + [ + print_preview.PrinterType.EXTENSION_PRINTER, + print_preview.DestinationStorePrinterSearchStatus.START + ], + [ + print_preview.PrinterType.PRIVET_PRINTER, + print_preview.DestinationStorePrinterSearchStatus.START + ], + [ + print_preview.PrinterType.LOCAL_PRINTER, + print_preview.DestinationStorePrinterSearchStatus.START + ] + ]); + + /** + * MDNS service name of destination that we are waiting to register. + * @private {?string} + */ + this.waitForRegisterDestination_ = null; + + /** + * Local destinations are CROS destinations on ChromeOS because they + * require extra setup. + * @private {!print_preview.DestinationOrigin} + */ + this.platformOrigin_ = cr.isChromeOS ? + print_preview.DestinationOrigin.CROS : + print_preview.DestinationOrigin.LOCAL; + + /** + * Whether to default to the system default printer instead of the most + * recent destination. + * @private {boolean} + */ + this.useSystemDefaultAsDefault_ = + loadTimeData.getBoolean('useSystemDefaultPrinter'); + + this.reset_(); + + this.addWebUIEventListeners_(listenerTracker); + } /** * @param {?string=} opt_account Account to filter destinations by. When @@ -506,14 +278,14 @@ cr.define('print_preview', function() { * @return {!Array<!print_preview.Destination>} List of destinations * accessible by the {@code account}. */ - destinations: function(opt_account) { + destinations(opt_account) { if (opt_account) { return this.destinations_.filter(function(destination) { return !destination.account || destination.account == opt_account; }); } return this.destinations_.slice(0); - }, + } /** * @return {print_preview.Destination} The currently selected destination or @@ -521,22 +293,23 @@ cr.define('print_preview', function() { */ get selectedDestination() { return this.selectedDestination_; - }, + } /** @return {boolean} Whether destination selection is pending or not. */ get isAutoSelectDestinationInProgress() { return this.selectedDestination_ == null && this.autoSelectTimeout_ != null; - }, + } /** * @return {boolean} Whether a search for local destinations is in progress. */ get isLocalDestinationSearchInProgress() { - return this.isLocalDestinationSearchInProgress_ || - this.isPrivetDestinationSearchInProgress_ || - this.isExtensionDestinationSearchInProgress_; - }, + return Array.from(this.destinationSearchStatus_.values()) + .some( + el => el === + print_preview.DestinationStorePrinterSearchStatus.SEARCHING); + } /** * @return {boolean} Whether a search for cloud destinations is in progress. @@ -544,7 +317,7 @@ cr.define('print_preview', function() { get isCloudDestinationSearchInProgress() { return !!this.cloudPrintInterface_ && this.cloudPrintInterface_.isCloudDestinationSearchInProgress; - }, + } /** * Starts listening for relevant WebUI events and adds the listeners to @@ -553,15 +326,11 @@ cr.define('print_preview', function() { * @param {!WebUIListenerTracker} listenerTracker * @private */ - addWebUIEventListeners_: function(listenerTracker) { - listenerTracker.add( - 'privet-printer-added', this.onPrivetPrinterAdded_.bind(this)); - listenerTracker.add( - 'extension-printers-added', - this.onExtensionPrintersAdded_.bind(this)); + addWebUIEventListeners_(listenerTracker) { + listenerTracker.add('printers-added', this.onPrintersAdded_.bind(this)); listenerTracker.add( 'reload-printer-list', this.onDestinationsReload.bind(this)); - }, + } /** * Initializes the destination store. Sets the initially selected @@ -570,12 +339,12 @@ cr.define('print_preview', function() { * print_preview.AppState has been initialized. * @param {boolean} isInAppKioskMode Whether the print preview is in App * Kiosk mode. - * @param {?string} systemDefaultDestinationId ID of the system default + * @param {string} systemDefaultDestinationId ID of the system default * destination. * @param {?string} serializedDefaultDestinationSelectionRulesStr Serialized * default destination selection rules. */ - init: function( + init( isInAppKioskMode, systemDefaultDestinationId, serializedDefaultDestinationSelectionRulesStr) { this.pdfPrinterEnabled_ = !isInAppKioskMode; @@ -591,7 +360,7 @@ cr.define('print_preview', function() { } } - if (!this.systemDefaultDestinationId_ && + if (this.systemDefaultDestinationId_.length == 0 && !this.appState_.isSelectedDestinationValid()) { this.selectPdfDestination_(); return; @@ -615,7 +384,7 @@ cr.define('print_preview', function() { origin = this.appState_.recentDestinations[i].origin; id = this.appState_.recentDestinations[i].id; account = this.appState_.recentDestinations[i].account || ''; - name = this.appState_.recentDestinations[i].name || ''; + name = this.appState_.recentDestinations[i].displayName || ''; capabilities = this.appState_.recentDestinations[i].capabilities; extensionId = this.appState_.recentDestinations[i].extensionId || ''; extensionName = @@ -639,7 +408,7 @@ cr.define('print_preview', function() { return; // Try the system default - id = this.systemDefaultDestinationId_ || ''; + id = this.systemDefaultDestinationId_; origin = id == print_preview.Destination.GooglePromotedId.SAVE_AS_PDF ? print_preview.DestinationOrigin.LOCAL : this.platformOrigin_; @@ -658,12 +427,12 @@ cr.define('print_preview', function() { } this.selectPdfDestination_(); - }, + } /** * Attempts to fetch capabilities of the destination identified by the * provided origin, id and account. - * @param {string | print_preview.DestinationOrigin} origin Destination + * @param {print_preview.DestinationOrigin} origin Destination * origin. * @param {string} id Destination id. * @param {string} account User account destination is registered for. @@ -676,67 +445,46 @@ cr.define('print_preview', function() { * @return {boolean} Whether capabilities fetch was successfully started. * @private */ - fetchPreselectedDestination_: function( + fetchPreselectedDestination_( origin, id, account, name, capabilities, extensionId, extensionName) { this.autoSelectMatchingDestination_ = this.createExactDestinationMatch_(origin, id); - if (origin == print_preview.DestinationOrigin.LOCAL || - origin == print_preview.DestinationOrigin.CROS) { - this.nativeLayer_.getPrinterCapabilities(id).then( - this.onLocalDestinationCapabilitiesSet_.bind(this), - this.onGetCapabilitiesFail_.bind( - this, - /** @type {print_preview.DestinationOrigin} */ (origin), id)); + var type = print_preview.originToType(origin); + if (type == print_preview.PrinterType.LOCAL_PRINTER) { + this.nativeLayer_.getPrinterCapabilities(id, type).then( + this.onCapabilitiesSet_.bind(this, origin, id), + this.onGetCapabilitiesFail_.bind(this, origin, id)); return true; } if (this.cloudPrintInterface_ && (origin == print_preview.DestinationOrigin.COOKIES || origin == print_preview.DestinationOrigin.DEVICE)) { - this.cloudPrintInterface_.printer( - id, - /** @type {print_preview.DestinationOrigin} */ (origin), account); + this.cloudPrintInterface_.printer(id, origin, account); return true; } - if (origin == print_preview.DestinationOrigin.PRIVET) { + if (origin == print_preview.DestinationOrigin.PRIVET || + origin == print_preview.DestinationOrigin.EXTENSION) { // TODO(noamsml): Resolve a specific printer instead of listing all - // privet printers in this case. - this.nativeLayer_.getPrivetPrinters().then( - this.endPrivetPrinterSearch_.bind(this)); + // privet or extension printers in this case. + this.startLoadDestinations(type); // Create a fake selectedDestination_ that is not actually in the // destination store. When the real destination is created, this // destination will be overwritten. - this.selectedDestination_ = new print_preview.Destination( - id, print_preview.DestinationType.LOCAL, - print_preview.DestinationOrigin.PRIVET, name, false /*isRecent*/, - print_preview.DestinationConnectionStatus.ONLINE); - - if (capabilities) { - this.selectedDestination_.capabilities = capabilities; - - cr.dispatchSimpleEvent( - this, - DestinationStore.EventType - .CACHED_SELECTED_DESTINATION_INFO_READY); - } - return true; - } - - if (origin == print_preview.DestinationOrigin.EXTENSION) { - // TODO(tbarzic): Add support for requesting a single extension's - // printer list. - this.startLoadExtensionDestinations(); - - this.selectedDestination_ = - print_preview.ExtensionDestinationParser.parse({ + var params = + (origin === print_preview.DestinationOrigin.PRIVET) ? {} : { + description: '', extensionId: extensionId, extensionName: extensionName, - id: id, - name: name - }); + provisionalType: print_preview.DestinationProvisionalType.NONE + }; + this.selectedDestination_ = new print_preview.Destination( + id, print_preview.DestinationType.LOCAL, origin, name, + false /*isRecent*/, + print_preview.DestinationConnectionStatus.ONLINE, params); if (capabilities) { this.selectedDestination_.capabilities = capabilities; @@ -748,37 +496,27 @@ cr.define('print_preview', function() { } return true; } - return false; - }, + } /** * Attempts to find a destination matching the provided rules. * @param {!print_preview.DestinationMatch} destinationMatch Rules to match. * @private */ - fetchMatchingDestination_: function(destinationMatch) { + fetchMatchingDestination_(destinationMatch) { this.autoSelectMatchingDestination_ = destinationMatch; - - if (destinationMatch.matchOrigin(print_preview.DestinationOrigin.LOCAL) || - destinationMatch.matchOrigin(print_preview.DestinationOrigin.CROS)) { - this.startLoadLocalDestinations(); - } - if (destinationMatch.matchOrigin( - print_preview.DestinationOrigin.PRIVET)) { - this.startLoadPrivetDestinations(); - } - if (destinationMatch.matchOrigin( - print_preview.DestinationOrigin.EXTENSION)) { - this.startLoadExtensionDestinations(); - } - if (destinationMatch.matchOrigin( + var type = destinationMatch.getType(); + if (type != null) { // Local, Privet, or Extension. + this.startLoadDestinations(type); + } else if ( + destinationMatch.matchOrigin( print_preview.DestinationOrigin.COOKIES) || destinationMatch.matchOrigin( print_preview.DestinationOrigin.DEVICE)) { this.startLoadCloudDestinations(); } - }, + } /** * @param {?string} serializedDefaultDestinationSelectionRulesStr Serialized @@ -787,8 +525,7 @@ cr.define('print_preview', function() { * previously selected destination. * @private */ - convertToDestinationMatch_: function( - serializedDefaultDestinationSelectionRulesStr) { + convertToDestinationMatch_(serializedDefaultDestinationSelectionRulesStr) { var matchRules = null; try { if (serializedDefaultDestinationSelectionRulesStr) { @@ -841,25 +578,25 @@ cr.define('print_preview', function() { return new print_preview.DestinationMatch( origins, idRegExp, displayNameRegExp, true /*skipVirtualDestinations*/); - }, + } /** * @return {print_preview.DestinationMatch} Creates rules matching * previously selected destination. * @private */ - convertPreselectedToDestinationMatch_: function() { + convertPreselectedToDestinationMatch_() { if (this.appState_.isSelectedDestinationValid()) { return this.createExactDestinationMatch_( this.appState_.selectedDestination.origin, this.appState_.selectedDestination.id); } - if (this.systemDefaultDestinationId_) { + if (this.systemDefaultDestinationId_.length > 0) { return this.createExactDestinationMatch_( this.platformOrigin_, this.systemDefaultDestinationId_); } return null; - }, + } /** * @param {string | print_preview.DestinationOrigin} origin Destination @@ -869,19 +606,19 @@ cr.define('print_preview', function() { * provided destination. * @private */ - createExactDestinationMatch_: function(origin, id) { + createExactDestinationMatch_(origin, id) { return new print_preview.DestinationMatch( [origin], new RegExp('^' + id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '$'), null /*displayNameRegExp*/, false /*skipVirtualDestinations*/); - }, + } /** * Sets the destination store's Google Cloud Print interface. * @param {!cloudprint.CloudPrintInterface} cloudPrintInterface Interface * to set. */ - setCloudPrintInterface: function(cloudPrintInterface) { + setCloudPrintInterface(cloudPrintInterface) { assert(this.cloudPrintInterface_ == null); this.cloudPrintInterface_ = cloudPrintInterface; this.tracker_.add( @@ -904,12 +641,12 @@ cr.define('print_preview', function() { this.cloudPrintInterface_, cloudprint.CloudPrintInterfaceEventType.PROCESS_INVITE_DONE, this.onCloudPrintProcessInviteDone_.bind(this)); - }, + } /** * @param {print_preview.Destination} destination Destination to select. */ - selectDestination: function(destination) { + selectDestination(destination) { this.autoSelectMatchingDestination_ = null; // When auto select expires, DESTINATION_SELECT event has to be dispatched // anyway (see isAutoSelectDestinationInProgress() logic). @@ -952,24 +689,14 @@ cr.define('print_preview', function() { // Request destination capabilities from backend, since they are not // known yet. if (destination.capabilities == null) { - if (destination.isPrivet) { - this.nativeLayer_.getPrivetPrinterCapabilities(destination.id) - .then( - this.onPrivetCapabilitiesSet_.bind(this), - this.onGetCapabilitiesFail_.bind( - this, destination.origin, destination.id)); - } else if (destination.isExtension) { - this.nativeLayer_.getExtensionPrinterCapabilities(destination.id) + var type = print_preview.originToType(destination.origin); + if (type !== null) { + this.nativeLayer_.getPrinterCapabilities(destination.id, type) .then( - this.onExtensionCapabilitiesSet_.bind(this, destination.id), - this.onGetCapabilitiesFail_.bind( - this, destination.origin, destination.id)); - } else if (destination.isLocal) { - this.nativeLayer_.getPrinterCapabilities(destination.id) - .then( - this.onLocalDestinationCapabilitiesSet_.bind(this), - this.onGetCapabilitiesFail_.bind( - this, destination.origin, destination.id)); + (caps) => this.onCapabilitiesSet_( + destination.origin, destination.id, caps), + () => this.onGetCapabilitiesFail_( + destination.origin, destination.origin)); } else { assert( this.cloudPrintInterface_ != null, @@ -982,7 +709,7 @@ cr.define('print_preview', function() { this, DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY); } - }, + } /** * Attempt to resolve the capabilities for a Chrome OS printer. @@ -990,17 +717,17 @@ cr.define('print_preview', function() { * requires resolution. * @return {!Promise<!print_preview.PrinterSetupResponse>} */ - resolveCrosDestination: function(destination) { + resolveCrosDestination(destination) { assert(destination.origin == print_preview.DestinationOrigin.CROS); return this.nativeLayer_.setupPrinter(destination.id); - }, + } /** * Attempts to resolve a provisional destination. * @param {!print_preview.Destination} destination Provisional destination * that should be resolved. */ - resolveProvisionalDestination: function(destination) { + resolveProvisionalDestination(destination) { assert( destination.provisionalType == print_preview.DestinationProvisionalType.NEEDS_USB_PERMISSION, @@ -1016,8 +743,7 @@ cr.define('print_preview', function() { */ this.removeProvisionalDestination_(destination.id); var parsedDestination = - print_preview.ExtensionDestinationParser.parse( - destinationInfo); + print_preview.parseExtensionDestination(destinationInfo); this.insertIntoStore_(parsedDestination); this.dispatchProvisionalDestinationResolvedEvent_( destination.id, parsedDestination); @@ -1032,27 +758,27 @@ cr.define('print_preview', function() { this.dispatchProvisionalDestinationResolvedEvent_( destination.id, null); }); - }, + } /** * Selects 'Save to PDF' destination (since it always exists). * @private */ - selectPdfDestination_: function() { + selectPdfDestination_() { var saveToPdfKey = this.getDestinationKey_( print_preview.DestinationOrigin.LOCAL, print_preview.Destination.GooglePromotedId.SAVE_AS_PDF, ''); this.selectDestination( this.destinationMap_[saveToPdfKey] || this.destinations_[0] || null); - }, + } /** * Attempts to select system default destination with a fallback to * 'Save to PDF' destination. * @private */ - selectDefaultDestination_: function() { - if (this.systemDefaultDestinationId_) { + selectDefaultDestination_() { + if (this.systemDefaultDestinationId_.length > 0) { if (this.autoSelectMatchingDestination_ && !this.autoSelectMatchingDestination_.matchIdAndOrigin( this.systemDefaultDestinationId_, this.platformOrigin_)) { @@ -1065,59 +791,37 @@ cr.define('print_preview', function() { } } this.selectPdfDestination_(); - }, - - /** Initiates loading of local print destinations. */ - startLoadLocalDestinations: function() { - if (!this.hasLoadedAllLocalDestinations_) { - this.hasLoadedAllLocalDestinations_ = true; - this.nativeLayer_.getPrinters().then( - this.onLocalDestinationsSet_.bind(this)); - this.isLocalDestinationSearchInProgress_ = true; - cr.dispatchSimpleEvent( - this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED); - } - }, + } - /** Initiates loading of privet print destinations. */ - startLoadPrivetDestinations: function() { - if (this.hasLoadedAllPrivetDestinations_) + /** + * Initiates loading of destinations. + * @param{print_preview.PrinterType} type The type of destinations to load. + */ + startLoadDestinations(type) { + if (this.destinationSearchStatus_.get(type) === + print_preview.DestinationStorePrinterSearchStatus.DONE) { return; - this.isPrivetDestinationSearchInProgress_ = true; - this.nativeLayer_.getPrivetPrinters().then( - this.endPrivetPrinterSearch_.bind(this), () => { - // Rejected by C++, indicating privet printing is disabled. - this.hasLoadedAllPrivetDestinations_ = true; - this.isPrivetDestinationSearchInProgress_ = false; + } + this.destinationSearchStatus_.set( + type, print_preview.DestinationStorePrinterSearchStatus.SEARCHING); + this.nativeLayer_.getPrinters(type).then( + this.onDestinationSearchDone_.bind(this, type), () => { + // Will be rejected by C++ for privet printers if privet printing + // is disabled. + assert(type === print_preview.PrinterType.PRIVET_PRINTER); + this.destinationSearchStatus_.set( + type, print_preview.DestinationStorePrinterSearchStatus.DONE); }); cr.dispatchSimpleEvent( this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED); - }, - - /** Initializes loading of extension managed print destinations. */ - startLoadExtensionDestinations: function() { - if (this.hasLoadedAllExtensionDestinations_) - return; - - if (this.isExtensionDestinationSearchInProgress_) - clearTimeout(this.extensionSearchTimeout_); - - this.isExtensionDestinationSearchInProgress_ = true; - this.nativeLayer_.getExtensionPrinters().then( - this.onExtensionPrintersDone_.bind(this)); - cr.dispatchSimpleEvent( - this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED); - this.extensionSearchTimeout_ = setTimeout( - this.endExtensionPrinterSearch_.bind(this), - DestinationStore.EXTENSION_SEARCH_DURATION_); - }, + } /** * Initiates loading of cloud destinations. * @param {print_preview.DestinationOrigin=} opt_origin Search destinations * for the specified origin only. */ - startLoadCloudDestinations: function(opt_origin) { + startLoadCloudDestinations(opt_origin) { if (this.cloudPrintInterface_ != null) { var origins = this.loadedCloudOrigins_[this.userInfo_.activeUser] || []; if (origins.length == 0 || @@ -1128,10 +832,10 @@ cr.define('print_preview', function() { this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED); } } - }, + } /** Requests load of COOKIE based cloud destinations. */ - reloadUserCookieBasedDestinations: function() { + reloadUserCookieBasedDestinations() { var origins = this.loadedCloudOrigins_[this.userInfo_.activeUser] || []; if (origins.indexOf(print_preview.DestinationOrigin.COOKIES) >= 0) { cr.dispatchSimpleEvent( @@ -1140,24 +844,26 @@ cr.define('print_preview', function() { this.startLoadCloudDestinations( print_preview.DestinationOrigin.COOKIES); } - }, + } /** Initiates loading of all known destination types. */ - startLoadAllDestinations: function() { + startLoadAllDestinations() { this.startLoadCloudDestinations(); - this.startLoadLocalDestinations(); - this.startLoadPrivetDestinations(); - this.startLoadExtensionDestinations(); - }, + for (var printerType of Object.values(print_preview.PrinterType)) { + if (printerType !== print_preview.PrinterType.PDF_PRINTER) + this.startLoadDestinations(printerType); + } + } /** * Wait for a privet device to be registered. */ - waitForRegister: function(id) { - this.nativeLayer_.getPrivetPrinters().then( - this.endPrivetPrinterSearch_.bind(this)); + waitForRegister(id) { + var privetType = print_preview.PrinterType.PRIVET_PRINTER; + this.nativeLayer_.getPrinters(privetType) + .then(this.onDestinationSearchDone_.bind(this, privetType)); this.waitForRegisterDestination_ = id; - }, + } /** * Removes the provisional destination with ID |provisionalId| from @@ -1165,7 +871,7 @@ cr.define('print_preview', function() { * @param{string} provisionalId The provisional destination ID. * @private */ - removeProvisionalDestination_: function(provisionalId) { + removeProvisionalDestination_(provisionalId) { this.destinations_ = this.destinations_.filter( function(el) { if (el.id == provisionalId) { @@ -1174,7 +880,7 @@ cr.define('print_preview', function() { } return true; }, this); - }, + } /** * Dispatches the PROVISIONAL_DESTINATION_RESOLVED event for id @@ -1184,14 +890,13 @@ cr.define('print_preview', function() { * @param {?print_preview.Destination} destination Information about the * destination if it was resolved successfully. */ - dispatchProvisionalDestinationResolvedEvent_: function( - provisionalId, destination) { + dispatchProvisionalDestinationResolvedEvent_(provisionalId, destination) { var event = new Event( DestinationStore.EventType.PROVISIONAL_DESTINATION_RESOLVED); event.provisionalId = provisionalId; event.destination = destination; this.dispatchEvent(event); - }, + } /** * Inserts {@code destination} to the data store and dispatches a @@ -1200,28 +905,36 @@ cr.define('print_preview', function() { * insert. * @private */ - insertDestination_: function(destination) { + insertDestination_(destination) { if (this.insertIntoStore_(destination)) { this.destinationsInserted_(destination); } - }, + } /** * Inserts multiple {@code destinations} to the data store and dispatches * single DESTINATIONS_INSERTED event. - * @param {!Array<print_preview.Destination>} destinations Print + * @param {!Array<!print_preview.Destination | + * !Array<print_preview.Destination>>} destinations Print * destinations to insert. * @private */ - insertDestinations_: function(destinations) { + insertDestinations_(destinations) { var inserted = false; - destinations.forEach(function(destination) { - inserted = this.insertIntoStore_(destination) || inserted; - }, this); + destinations.forEach(destination => { + if (Array.isArray(destination)) { + // privet printers return arrays of 1 or 2 printers + inserted = destination.reduce(function(soFar, d) { + return this.insertIntoStore_(d) || soFar; + }, inserted); + } else { + inserted = this.insertIntoStore_(destination) || inserted; + } + }); if (inserted) { this.destinationsInserted_(); } - }, + } /** * Dispatches DESTINATIONS_INSERTED event. In auto select mode, tries to @@ -1232,7 +945,7 @@ cr.define('print_preview', function() { * changed. Used as a hint to limit destination search scope against * {@code autoSelectMatchingDestination_}. */ - destinationsInserted_: function(opt_destination) { + destinationsInserted_(opt_destination) { cr.dispatchSimpleEvent( this, DestinationStore.EventType.DESTINATIONS_INSERTED); if (this.autoSelectMatchingDestination_) { @@ -1245,7 +958,7 @@ cr.define('print_preview', function() { } }, this); } - }, + } /** * Updates an existing print destination with capabilities and display name @@ -1253,12 +966,14 @@ cr.define('print_preview', function() { * @param {!print_preview.Destination} destination Destination to update. * @private */ - updateDestination_: function(destination) { + updateDestination_(destination) { assert(destination.constructor !== Array, 'Single printer expected'); - destination.capabilities_ = DestinationStore.localizeCapabilities_( - assert(destination.capabilities_)); destination.capabilities_ = - DestinationStore.sortMediaSizes_(destination.capabilities_); + localizeCapabilities(assert(destination.capabilities_)); + if (print_preview.originToType(destination.origin) !== + print_preview.PrinterType.LOCAL_PRINTER) { + destination.capabilities_ = sortMediaSizes(destination.capabilities_); + } var existingDestination = this.destinationMap_[this.getKey_(destination)]; if (existingDestination != null) { existingDestination.capabilities = destination.capabilities; @@ -1274,28 +989,13 @@ cr.define('print_preview', function() { this, DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY); } - }, - - /** - * Called when the search for Privet printers is done. - * @private - */ - endPrivetPrinterSearch_: function() { - this.isPrivetDestinationSearchInProgress_ = false; - this.hasLoadedAllPrivetDestinations_ = true; - cr.dispatchSimpleEvent( - this, DestinationStore.EventType.DESTINATION_SEARCH_DONE); - }, + } /** * Called when loading of extension managed printers is done. * @private */ - endExtensionPrinterSearch_: function() { - this.isExtensionDestinationSearchInProgress_ = false; - this.hasLoadedAllExtensionDestinations_ = true; - cr.dispatchSimpleEvent( - this, DestinationStore.EventType.DESTINATION_SEARCH_DONE); + endExtensionPrinterSearch_() { // Clear initially selected (cached) extension destination if it hasn't // been found among reported extension destinations. if (this.autoSelectMatchingDestination_ && @@ -1304,15 +1004,17 @@ cr.define('print_preview', function() { this.selectedDestination_ && this.selectedDestination_.isExtension) { this.selectDefaultDestination_(); } - }, + } /** * Inserts a destination into the store without dispatching any events. + * @param {!print_preview.Destination} destination The destination to be + * inserted. * @return {boolean} Whether the inserted destination was not already in the * store. * @private */ - insertIntoStore_: function(destination) { + insertIntoStore_(destination) { var key = this.getKey_(destination); var existingDestination = this.destinationMap_[key]; if (existingDestination == null) { @@ -1334,13 +1036,13 @@ cr.define('print_preview', function() { return true; } return false; - }, + } /** * Creates a local PDF print destination. * @private */ - createLocalPdfPrintDestination_: function() { + createLocalPdfPrintDestination_() { // TODO(alekseys): Create PDF printer in the native code and send its // capabilities back with other local printers. if (this.pdfPrinterEnabled_) { @@ -1351,100 +1053,86 @@ cr.define('print_preview', function() { loadTimeData.getString('printToPDF'), false /*isRecent*/, print_preview.DestinationConnectionStatus.ONLINE)); } - }, + } /** * Resets the state of the destination store to its initial state. * @private */ - reset_: function() { + reset_() { this.destinations_ = []; this.destinationMap_ = {}; this.selectDestination(null); this.loadedCloudOrigins_ = {}; - this.hasLoadedAllLocalDestinations_ = false; - this.hasLoadedAllPrivetDestinations_ = false; - this.hasLoadedAllExtensionDestinations_ = false; + for (var printerType of Object.values(print_preview.PrinterType)) { + if (printerType !== print_preview.PrinterType.PDF_PRINTER) { + this.destinationSearchStatus_.set( + printerType, + print_preview.DestinationStorePrinterSearchStatus.START); + } + } clearTimeout(this.autoSelectTimeout_); this.autoSelectTimeout_ = setTimeout( this.selectDefaultDestination_.bind(this), DestinationStore.AUTO_SELECT_TIMEOUT_); - }, + } + /** - * Called when the local destinations have been got from the native layer. - * @param {!Array<!print_preview.LocalDestinationInfo>} destinationInfos A - * list of the local destinations retrieved. - * @private + * Called when destination search is complete for some type of printer. + * @param {!print_preview.PrinterType} type The type of printers that are + * done being retreived. */ - onLocalDestinationsSet_: function(destinationInfos) { - var localDestinations = destinationInfos.map(function(destInfo) { - return print_preview.LocalDestinationParser.parse(destInfo); - }); - this.insertDestinations_(localDestinations); - this.isLocalDestinationSearchInProgress_ = false; + onDestinationSearchDone_(type) { + this.destinationSearchStatus_.set( + type, print_preview.DestinationStorePrinterSearchStatus.DONE); cr.dispatchSimpleEvent( this, DestinationStore.EventType.DESTINATION_SEARCH_DONE); - }, + if (type === print_preview.PrinterType.EXTENSION_PRINTER) + this.endExtensionPrinterSearch_(); + } /** * Called when the native layer retrieves the capabilities for the selected * local destination. Updates the destination with new capabilities if the * destination already exists, otherwise it creates a new destination and * then updates its capabilities. - * @param {print_preview.PrinterCapabilitiesResponse} settingsInfo Contains - * information about and capabilities of the local print destination. + * @param {!print_preview.DestinationOrigin} origin The origin of the + * print destination. + * @param {string} id The id of the print destination. + * @param {!print_preview.CapabilitiesResponse} settingsInfo Contains + * the capabilities of the print destination, and information about + * the destination except in the case of extension printers. * @private */ - onLocalDestinationCapabilitiesSet_: function(settingsInfo) { - var destinationId = settingsInfo['printerId']; - var printerName = settingsInfo['printerName']; - var printerDescription = settingsInfo['printerDescription']; - // PDF is special since we don't need to query the device for - // capabilities. - var origin = destinationId == - print_preview.Destination.GooglePromotedId.SAVE_AS_PDF ? - print_preview.DestinationOrigin.LOCAL : - this.platformOrigin_; - var key = this.getDestinationKey_(origin, destinationId, ''); - var destination = this.destinationMap_[key]; - var capabilities = - DestinationStore.localizeCapabilities_(settingsInfo.capabilities); - // Special case for PDF printer (until local printers capabilities are - // reported in CDD format too). - if (destinationId == - print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) { - if (destination) { - destination.capabilities = capabilities; + onCapabilitiesSet_(origin, id, settingsInfo) { + var dest = null; + if (origin !== print_preview.DestinationOrigin.PRIVET) { + var key = this.getDestinationKey_(origin, id, ''); + dest = this.destinationMap_[key]; + } + if (!dest) { + // Ignore unrecognized extension printers + if (!settingsInfo.printer) { + assert(origin === print_preview.DestinationOrigin.EXTENSION); + return; } - } else { - if (destination) { - // In case there were multiple capabilities request for this local - // destination, just ignore the later ones. - if (destination.capabilities != null) { - return; - } - destination.capabilities = capabilities; + dest = print_preview.parseDestination( + print_preview.originToType(origin), assert(settingsInfo.printer)); + } + if (dest) { + var updateDestination = destination => { + destination.capabilities = settingsInfo.capabilities; + this.updateDestination_(destination); + }; + if (Array.isArray(dest)) { + dest.forEach(updateDestination); } else { - var isEnterprisePrinter = settingsInfo['cupsEnterprisePrinter']; - destination = print_preview.LocalDestinationParser.parse({ - deviceName: destinationId, - printerName: printerName, - cupsEnterprisePrinter: isEnterprisePrinter, - printerDescription: printerDescription - }); - destination.capabilities = capabilities; - this.insertDestination_(destination); + updateDestination(dest); } } - if (this.selectedDestination_ && - this.selectedDestination_.id == destinationId) { - cr.dispatchSimpleEvent( - this, - DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY); - } - }, + } /** * Called when a request to get a local destination's print capabilities @@ -1455,15 +1143,22 @@ cr.define('print_preview', function() { * @param {string} destinationId The destination ID that failed. * @private */ - onGetCapabilitiesFail_: function(origin, destinationId) { + onGetCapabilitiesFail_(origin, destinationId) { console.warn( 'Failed to get print capabilities for printer ' + destinationId); + if (this.selectedDestination_ && + this.selectedDestination_.id == destinationId) { + var event = + new Event(DestinationStore.EventType.SELECTED_DESTINATION_INVALID); + event.destinationId = destinationId; + this.dispatchEvent(event); + } if (this.autoSelectMatchingDestination_ && this.autoSelectMatchingDestination_.matchIdAndOrigin( destinationId, origin)) { this.selectDefaultDestination_(); } - }, + } /** * Called when the /search call completes, either successfully or not. @@ -1471,7 +1166,7 @@ cr.define('print_preview', function() { * @param {Event} event Contains the request result. * @private */ - onCloudPrintSearchDone_: function(event) { + onCloudPrintSearchDone_(event) { if (event.printers) { this.insertDestinations_(event.printers); } @@ -1483,7 +1178,7 @@ cr.define('print_preview', function() { } cr.dispatchSimpleEvent( this, DestinationStore.EventType.DESTINATION_SEARCH_DONE); - }, + } /** * Called when /printer call completes. Updates the specified destination's @@ -1492,9 +1187,9 @@ cr.define('print_preview', function() { * destination. * @private */ - onCloudPrintPrinterDone_: function(event) { + onCloudPrintPrinterDone_(event) { this.updateDestination_(event.printer); - }, + } /** * Called when the Google Cloud Print interface fails to lookup a @@ -1504,7 +1199,7 @@ cr.define('print_preview', function() { * to be looked up. * @private */ - onCloudPrintPrinterFailed_: function(event) { + onCloudPrintPrinterFailed_(event) { if (this.autoSelectMatchingDestination_ && this.autoSelectMatchingDestination_.matchIdAndOrigin( event.destinationId, event.destinationOrigin)) { @@ -1512,7 +1207,7 @@ cr.define('print_preview', function() { 'Failed to fetch last used printer caps: ' + event.destinationId); this.selectDefaultDestination_(); } - }, + } /** * Called when printer sharing invitation was processed successfully. @@ -1520,105 +1215,49 @@ cr.define('print_preview', function() { * newly accepted destination (if known). * @private */ - onCloudPrintProcessInviteDone_: function(event) { + onCloudPrintProcessInviteDone_(event) { if (event.accept && event.printer) { // Hint the destination list to promote this new destination. event.printer.isRecent = true; this.insertDestination_(event.printer); } - }, - - /** - * Called when a Privet printer is added to the local network. - * @param {!{serviceName: string, - * name: string, - * hasLocalPrinting: boolean, - * isUnregistered: boolean, - * cloudID: string}} printer Information about the added printer. - * @private - */ - onPrivetPrinterAdded_: function(printer) { - if (printer.serviceName == this.waitForRegisterDestination_ && - !printer.isUnregistered) { - this.waitForRegisterDestination_ = null; - this.onDestinationsReload(); - } else { - this.insertDestinations_( - print_preview.PrivetDestinationParser.parse(printer)); - } - }, - - /** - * Called when capabilities for a privet printer are set. - * @param {!print_preview.PrivetPrinterCapabilitiesResponse} printerInfo - * Contains the privet printer's description and capabilities. - * @private - */ - onPrivetCapabilitiesSet_: function(printerInfo) { - var destinations = - print_preview.PrivetDestinationParser.parse(printerInfo.printer); - destinations.forEach(function(dest) { - dest.capabilities = printerInfo.capabilities; - this.updateDestination_(dest); - }, this); - }, - - /** - * Called when an extension responds to a getExtensionDestinations - * request. - * @param {!Array<!{extensionId: string, - * extensionName: string, - * id: string, - * name: string, - * description: (string|undefined), - * provisional: (boolean|undefined)}>} printers The list - * containing information about printers added by an extension. - * @private - */ - onExtensionPrintersAdded_: function(printers) { - this.insertDestinations_( - printers.map(print_preview.ExtensionDestinationParser.parse)); - }, + } /** - * Called when all extensions are done adding printers. - * @private - */ - onExtensionPrintersDone_: function() { - if (this.isExtensionDestinationSearchInProgress_) { - clearTimeout(this.extensionSearchTimeout_); - this.endExtensionPrinterSearch_(); + * Called when a printer or printers are detected after sending getPrinters + * from the native layer. + * @param {print_preview.PrinterType} type The type of printer(s) added. + * @param {!Array<!print_preview.LocalDestinationInfo | + * !print_preview.PrivetPrinterDescription | + * !print_preview.ProvisionalDestinationInfo>} printers + * Information about the printers that have been retrieved. + */ + onPrintersAdded_(type, printers) { + if (type == print_preview.PrinterType.PRIVET_PRINTER) { + var printer = + /** !print_preview.PrivetPrinterDescription */ (printers[0]); + if (printer.serviceName == this.waitForRegisterDestination_ && + !printer.isUnregistered) { + this.waitForRegisterDestination_ = null; + this.onDestinationsReload(); + return; + } } - }, - - /** - * Called when capabilities for an extension managed printer are set. - * @param {string} printerId The printer Id. - * @param {!print_preview.Cdd} capabilities The printer's capabilities. - * @private - */ - onExtensionCapabilitiesSet_: function(printerId, capabilities) { - var destinationKey = this.getDestinationKey_( - print_preview.DestinationOrigin.EXTENSION, printerId, - '' /* account */); - var destination = this.destinationMap_[destinationKey]; - if (!destination) - return; - destination.capabilities = capabilities; - this.updateDestination_(destination); - }, + this.insertDestinations_(printers.map( + printer => print_preview.parseDestination(type, printer))); + } /** * Called from print preview after the user was requested to sign in, and * did so successfully. */ - onDestinationsReload: function() { + onDestinationsReload() { this.reset_(); this.autoSelectMatchingDestination_ = this.convertPreselectedToDestinationMatch_(); this.createLocalPdfPrintDestination_(); this.startLoadAllDestinations(); - }, + } // TODO(vitalybuka): Remove three next functions replacing Destination.id // and Destination.origin by complex ID. @@ -1630,19 +1269,232 @@ cr.define('print_preview', function() { * @param {string} account User account destination is registered for. * @private */ - getDestinationKey_: function(origin, id, account) { + getDestinationKey_(origin, id, account) { return origin + '/' + id + '/' + account; - }, + } /** * Returns key to be used with {@code destinationMap_}. * @param {!print_preview.Destination} destination Destination. * @private */ - getKey_: function(destination) { + getKey_(destination) { return this.getDestinationKey_( destination.origin, destination.id, destination.account); } + } + + /** + * Event types dispatched by the data store. + * @enum {string} + */ + DestinationStore.EventType = { + DESTINATION_SEARCH_DONE: + 'print_preview.DestinationStore.DESTINATION_SEARCH_DONE', + DESTINATION_SEARCH_STARTED: + 'print_preview.DestinationStore.DESTINATION_SEARCH_STARTED', + DESTINATION_SELECT: 'print_preview.DestinationStore.DESTINATION_SELECT', + DESTINATIONS_INSERTED: + 'print_preview.DestinationStore.DESTINATIONS_INSERTED', + PROVISIONAL_DESTINATION_RESOLVED: + 'print_preview.DestinationStore.PROVISIONAL_DESTINATION_RESOLVED', + CACHED_SELECTED_DESTINATION_INFO_READY: + 'print_preview.DestinationStore.CACHED_SELECTED_DESTINATION_INFO_READY', + SELECTED_DESTINATION_CAPABILITIES_READY: 'print_preview.DestinationStore' + + '.SELECTED_DESTINATION_CAPABILITIES_READY', + SELECTED_DESTINATION_INVALID: + 'print_preview.DestinationStore.SELECTED_DESTINATION_INVALID', + }; + + /** + * Delay in milliseconds before the destination store ignores the initial + * destination ID and just selects any printer (since the initial destination + * was not found). + * @private {number} + * @const + */ + DestinationStore.AUTO_SELECT_TIMEOUT_ = 15000; + + /** + * Maximum amount of time spent searching for extension destinations, in + * milliseconds. + * @private {number} + * @const + */ + DestinationStore.EXTENSION_SEARCH_DURATION_ = 5000; + + /** + * Human readable names for media sizes in the cloud print CDD. + * https://developers.google.com/cloud-print/docs/cdd + * @private {Object<string>} + * @const + */ + DestinationStore.MEDIA_DISPLAY_NAMES_ = { + 'ISO_2A0': '2A0', + 'ISO_A0': 'A0', + 'ISO_A0X3': 'A0x3', + 'ISO_A1': 'A1', + 'ISO_A10': 'A10', + 'ISO_A1X3': 'A1x3', + 'ISO_A1X4': 'A1x4', + 'ISO_A2': 'A2', + 'ISO_A2X3': 'A2x3', + 'ISO_A2X4': 'A2x4', + 'ISO_A2X5': 'A2x5', + 'ISO_A3': 'A3', + 'ISO_A3X3': 'A3x3', + 'ISO_A3X4': 'A3x4', + 'ISO_A3X5': 'A3x5', + 'ISO_A3X6': 'A3x6', + 'ISO_A3X7': 'A3x7', + 'ISO_A3_EXTRA': 'A3 Extra', + 'ISO_A4': 'A4', + 'ISO_A4X3': 'A4x3', + 'ISO_A4X4': 'A4x4', + 'ISO_A4X5': 'A4x5', + 'ISO_A4X6': 'A4x6', + 'ISO_A4X7': 'A4x7', + 'ISO_A4X8': 'A4x8', + 'ISO_A4X9': 'A4x9', + 'ISO_A4_EXTRA': 'A4 Extra', + 'ISO_A4_TAB': 'A4 Tab', + 'ISO_A5': 'A5', + 'ISO_A5_EXTRA': 'A5 Extra', + 'ISO_A6': 'A6', + 'ISO_A7': 'A7', + 'ISO_A8': 'A8', + 'ISO_A9': 'A9', + 'ISO_B0': 'B0', + 'ISO_B1': 'B1', + 'ISO_B10': 'B10', + 'ISO_B2': 'B2', + 'ISO_B3': 'B3', + 'ISO_B4': 'B4', + 'ISO_B5': 'B5', + 'ISO_B5_EXTRA': 'B5 Extra', + 'ISO_B6': 'B6', + 'ISO_B6C4': 'B6C4', + 'ISO_B7': 'B7', + 'ISO_B8': 'B8', + 'ISO_B9': 'B9', + 'ISO_C0': 'C0', + 'ISO_C1': 'C1', + 'ISO_C10': 'C10', + 'ISO_C2': 'C2', + 'ISO_C3': 'C3', + 'ISO_C4': 'C4', + 'ISO_C5': 'C5', + 'ISO_C6': 'C6', + 'ISO_C6C5': 'C6C5', + 'ISO_C7': 'C7', + 'ISO_C7C6': 'C7C6', + 'ISO_C8': 'C8', + 'ISO_C9': 'C9', + 'ISO_DL': 'Envelope DL', + 'ISO_RA0': 'RA0', + 'ISO_RA1': 'RA1', + 'ISO_RA2': 'RA2', + 'ISO_SRA0': 'SRA0', + 'ISO_SRA1': 'SRA1', + 'ISO_SRA2': 'SRA2', + 'JIS_B0': 'B0 (JIS)', + 'JIS_B1': 'B1 (JIS)', + 'JIS_B10': 'B10 (JIS)', + 'JIS_B2': 'B2 (JIS)', + 'JIS_B3': 'B3 (JIS)', + 'JIS_B4': 'B4 (JIS)', + 'JIS_B5': 'B5 (JIS)', + 'JIS_B6': 'B6 (JIS)', + 'JIS_B7': 'B7 (JIS)', + 'JIS_B8': 'B8 (JIS)', + 'JIS_B9': 'B9 (JIS)', + 'JIS_EXEC': 'Executive (JIS)', + 'JPN_CHOU2': 'Choukei 2', + 'JPN_CHOU3': 'Choukei 3', + 'JPN_CHOU4': 'Choukei 4', + 'JPN_HAGAKI': 'Hagaki', + 'JPN_KAHU': 'Kahu Envelope', + 'JPN_KAKU2': 'Kaku 2', + 'JPN_OUFUKU': 'Oufuku Hagaki', + 'JPN_YOU4': 'You 4', + 'NA_10X11': '10x11', + 'NA_10X13': '10x13', + 'NA_10X14': '10x14', + 'NA_10X15': '10x15', + 'NA_11X12': '11x12', + 'NA_11X15': '11x15', + 'NA_12X19': '12x19', + 'NA_5X7': '5x7', + 'NA_6X9': '6x9', + 'NA_7X9': '7x9', + 'NA_9X11': '9x11', + 'NA_A2': 'A2', + 'NA_ARCH_A': 'Arch A', + 'NA_ARCH_B': 'Arch B', + 'NA_ARCH_C': 'Arch C', + 'NA_ARCH_D': 'Arch D', + 'NA_ARCH_E': 'Arch E', + 'NA_ASME_F': 'ASME F', + 'NA_B_PLUS': 'B-plus', + 'NA_C': 'C', + 'NA_C5': 'C5', + 'NA_D': 'D', + 'NA_E': 'E', + 'NA_EDP': 'EDP', + 'NA_EUR_EDP': 'European EDP', + 'NA_EXECUTIVE': 'Executive', + 'NA_F': 'F', + 'NA_FANFOLD_EUR': 'FanFold European', + 'NA_FANFOLD_US': 'FanFold US', + 'NA_FOOLSCAP': 'FanFold German Legal', + 'NA_GOVT_LEGAL': 'Government Legal', + 'NA_GOVT_LETTER': 'Government Letter', + 'NA_INDEX_3X5': 'Index 3x5', + 'NA_INDEX_4X6': 'Index 4x6', + 'NA_INDEX_4X6_EXT': 'Index 4x6 ext', + 'NA_INDEX_5X8': '5x8', + 'NA_INVOICE': 'Invoice', + 'NA_LEDGER': 'Tabloid', // Ledger in portrait is called Tabloid. + 'NA_LEGAL': 'Legal', + 'NA_LEGAL_EXTRA': 'Legal extra', + 'NA_LETTER': 'Letter', + 'NA_LETTER_EXTRA': 'Letter extra', + 'NA_LETTER_PLUS': 'Letter plus', + 'NA_MONARCH': 'Monarch', + 'NA_NUMBER_10': 'Envelope #10', + 'NA_NUMBER_11': 'Envelope #11', + 'NA_NUMBER_12': 'Envelope #12', + 'NA_NUMBER_14': 'Envelope #14', + 'NA_NUMBER_9': 'Envelope #9', + 'NA_PERSONAL': 'Personal', + 'NA_QUARTO': 'Quarto', + 'NA_SUPER_A': 'Super A', + 'NA_SUPER_B': 'Super B', + 'NA_WIDE_FORMAT': 'Wide format', + 'OM_DAI_PA_KAI': 'Dai-pa-kai', + 'OM_FOLIO': 'Folio', + 'OM_FOLIO_SP': 'Folio SP', + 'OM_INVITE': 'Invite Envelope', + 'OM_ITALIAN': 'Italian Envelope', + 'OM_JUURO_KU_KAI': 'Juuro-ku-kai', + 'OM_LARGE_PHOTO': 'Large photo', + 'OM_OFICIO': 'Oficio', + 'OM_PA_KAI': 'Pa-kai', + 'OM_POSTFIX': 'Postfix Envelope', + 'OM_SMALL_PHOTO': 'Small photo', + 'PRC_1': 'prc1 Envelope', + 'PRC_10': 'prc10 Envelope', + 'PRC_16K': 'prc 16k', + 'PRC_2': 'prc2 Envelope', + 'PRC_3': 'prc3 Envelope', + 'PRC_32K': 'prc 32k', + 'PRC_4': 'prc4 Envelope', + 'PRC_5': 'prc5 Envelope', + 'PRC_6': 'prc6 Envelope', + 'PRC_7': 'prc7 Envelope', + 'PRC_8': 'prc8 Envelope', + 'ROC_16K': 'ROC 16K', + 'ROC_8K': 'ROC 8k', }; // Export diff --git a/chromium/chrome/browser/resources/print_preview/data/invitation.js b/chromium/chrome/browser/resources/print_preview/data/invitation.js index 8aef4f3b23c..6f5d09de7ee 100644 --- a/chromium/chrome/browser/resources/print_preview/data/invitation.js +++ b/chromium/chrome/browser/resources/print_preview/data/invitation.js @@ -5,62 +5,61 @@ cr.define('print_preview', function() { 'use strict'; - /** - * Printer sharing invitation data object. - * @param {string} sender Text identifying invitation sender. - * @param {string} receiver Text identifying invitation receiver. Empty in - * case of a personal invitation. Identifies a group or domain in case - * of an invitation received by a group manager. - * @param {!print_preview.Destination} destination Shared destination. - * @param {!Object} aclEntry JSON representation of the ACL entry this - * invitation was sent to. - * @param {string} account User account this invitation is sent for. - * @constructor - */ - function Invitation(sender, receiver, destination, aclEntry, account) { + class Invitation { /** - * Text identifying invitation sender. - * @private {string} + * Printer sharing invitation data object. + * @param {string} sender Text identifying invitation sender. + * @param {string} receiver Text identifying invitation receiver. Empty in + * case of a personal invitation. Identifies a group or domain in case + * of an invitation received by a group manager. + * @param {!print_preview.Destination} destination Shared destination. + * @param {!Object} aclEntry JSON representation of the ACL entry this + * invitation was sent to. + * @param {string} account User account this invitation is sent for. */ - this.sender_ = sender; + constructor(sender, receiver, destination, aclEntry, account) { + /** + * Text identifying invitation sender. + * @private {string} + */ + this.sender_ = sender; - /** - * Text identifying invitation receiver. Empty in case of a personal - * invitation. Identifies a group or domain in case of an invitation - * received by a group manager. - * @private {string} - */ - this.receiver_ = receiver; + /** + * Text identifying invitation receiver. Empty in case of a personal + * invitation. Identifies a group or domain in case of an invitation + * received by a group manager. + * @private {string} + */ + this.receiver_ = receiver; - /** - * Shared destination. - * @private {!print_preview.Destination} - */ - this.destination_ = destination; + /** + * Shared destination. + * @private {!print_preview.Destination} + */ + this.destination_ = destination; - /** - * JSON representation of the ACL entry this invitation was sent to. - * @private {!Object} - */ - this.aclEntry_ = aclEntry; + /** + * JSON representation of the ACL entry this invitation was sent to. + * @private {!Object} + */ + this.aclEntry_ = aclEntry; - /** - * Account this invitation is sent for. - * @private {string} - */ - this.account_ = account; - } + /** + * Account this invitation is sent for. + * @private {string} + */ + this.account_ = account; + } - Invitation.prototype = { /** @return {string} Text identifying invitation sender. */ get sender() { return this.sender_; - }, + } /** @return {string} Text identifying invitation receiver. */ get receiver() { return this.receiver_; - }, + } /** * @return {boolean} Whether this user acts as a manager for a group of @@ -68,23 +67,23 @@ cr.define('print_preview', function() { */ get asGroupManager() { return !!this.receiver_; - }, + } /** @return {!print_preview.Destination} Shared destination. */ get destination() { return this.destination_; - }, + } /** @return {string} Scope (account) this invitation was sent to. */ get scopeId() { return this.aclEntry_['scope'] || ''; - }, + } /** @return {string} Account this invitation is sent for. */ get account() { return this.account_; } - }; + } // Export return {Invitation: Invitation}; diff --git a/chromium/chrome/browser/resources/print_preview/data/local_parsers.js b/chromium/chrome/browser/resources/print_preview/data/local_parsers.js index 8748f37b625..9b09d62e06f 100644 --- a/chromium/chrome/browser/resources/print_preview/data/local_parsers.js +++ b/chromium/chrome/browser/resources/print_preview/data/local_parsers.js @@ -5,8 +5,33 @@ cr.define('print_preview', function() { 'use strict'; - /** Namespace that contains a method to parse local print destinations. */ - function LocalDestinationParser() {} + /** + * @param{!print_preview.PrinterType} type The type of printer to parse. + * @param{!print_preview.LocalDestinationInfo | + * !print_preview.PrivetPrinterDescription | + * !print_preview.ProvisionalDestinationInfo} printer Information + * about the printer. Type expected depends on |type|: + * For LOCAL_PRINTER => print_preview.LocalDestinationInfo + * For PRIVET_PRINTER => print_preview.PrivetPrinterDescription + * For EXTENSION_PRINTER => print_preview.ProvisionalDestinationInfo + * @return {!Array<!print_preview.Destination> | !print_preview.Destination} + */ + var parseDestination = function(type, printer) { + if (type === print_preview.PrinterType.LOCAL_PRINTER) { + return parseLocalDestination( + /** @type {!print_preview.LocalDestinationInfo} */ (printer)); + } + if (type === print_preview.PrinterType.PRIVET_PRINTER) { + return parsePrivetDestination( + /** @type {!print_preview.PrivetPrinterDescription} */ (printer)); + } + if (type === print_preview.PrinterType.EXTENSION_PRINTER) { + return parseExtensionDestination( + /** @type {!print_preview.ProvisionalDestinationInfo} */ (printer)); + } + assertNotReached('Unknown printer type ' + type); + return []; + }; /** * Parses a local print destination. @@ -14,7 +39,7 @@ cr.define('print_preview', function() { * describing a local print destination. * @return {!print_preview.Destination} Parsed local print destination. */ - LocalDestinationParser.parse = function(destinationInfo) { + var parseLocalDestination = function(destinationInfo) { var options = { description: destinationInfo.printerDescription, isEnterprisePrinter: destinationInfo.cupsEnterprisePrinter @@ -34,15 +59,14 @@ cr.define('print_preview', function() { print_preview.DestinationConnectionStatus.ONLINE, options); }; - function PrivetDestinationParser() {} - /** * Parses a privet destination as one or more local printers. * @param {!print_preview.PrivetPrinterDescription} destinationInfo Object * that describes a privet printer. - * @return {!Array<!print_preview.Destination>} Parsed destination info. + * @return {!print_preview.Destination | + * !Array<!print_preview.Destination>} Parsed destination info. */ - PrivetDestinationParser.parse = function(destinationInfo) { + var parsePrivetDestination = function(destinationInfo) { var returnedPrinters = []; if (destinationInfo.hasLocalPrinting) { @@ -61,11 +85,10 @@ cr.define('print_preview', function() { print_preview.DestinationConnectionStatus.UNREGISTERED)); } - return returnedPrinters; + return returnedPrinters.length === 1 ? returnedPrinters[0] : + returnedPrinters; }; - function ExtensionDestinationParser() {} - /** * Parses an extension destination from an extension supplied printer * description. @@ -73,7 +96,7 @@ cr.define('print_preview', function() { * describing an extension printer. * @return {!print_preview.Destination} Parsed destination. */ - ExtensionDestinationParser.parse = function(destinationInfo) { + var parseExtensionDestination = function(destinationInfo) { var provisionalType = destinationInfo.provisional ? print_preview.DestinationProvisionalType.NEEDS_USB_PERMISSION : print_preview.DestinationProvisionalType.NONE; @@ -92,8 +115,7 @@ cr.define('print_preview', function() { // Export return { - LocalDestinationParser: LocalDestinationParser, - PrivetDestinationParser: PrivetDestinationParser, - ExtensionDestinationParser: ExtensionDestinationParser + parseDestination: parseDestination, + parseExtensionDestination: parseExtensionDestination }; }); diff --git a/chromium/chrome/browser/resources/print_preview/data/margins.js b/chromium/chrome/browser/resources/print_preview/data/margins.js index ba8239b9210..b7bef50c686 100644 --- a/chromium/chrome/browser/resources/print_preview/data/margins.js +++ b/chromium/chrome/browser/resources/print_preview/data/margins.js @@ -5,54 +5,55 @@ cr.define('print_preview', function() { 'use strict'; - /** - * Creates a Margins object that holds four margin values in points. - * @param {number} top The top margin in pts. - * @param {number} right The right margin in pts. - * @param {number} bottom The bottom margin in pts. - * @param {number} left The left margin in pts. - * @constructor - */ - function Margins(top, right, bottom, left) { + class Margins { /** - * Backing store for the margin values in points. - * @type {!Object< - * !print_preview.ticket_items.CustomMarginsOrientation, number>} - * @private + * Creates a Margins object that holds four margin values in points. + * @param {number} top The top margin in pts. + * @param {number} right The right margin in pts. + * @param {number} bottom The bottom margin in pts. + * @param {number} left The left margin in pts. */ - this.value_ = {}; - this.value_[print_preview.ticket_items.CustomMarginsOrientation.TOP] = top; - this.value_[print_preview.ticket_items.CustomMarginsOrientation.RIGHT] = - right; - this.value_[print_preview.ticket_items.CustomMarginsOrientation.BOTTOM] = - bottom; - this.value_[print_preview.ticket_items.CustomMarginsOrientation.LEFT] = - left; - } + constructor(top, right, bottom, left) { + /** + * Backing store for the margin values in points. + * @type {!Object< + * !print_preview.ticket_items.CustomMarginsOrientation, number>} + * @private + */ + this.value_ = {}; + this.value_[print_preview.ticket_items.CustomMarginsOrientation.TOP] = + top; + this.value_[print_preview.ticket_items.CustomMarginsOrientation.RIGHT] = + right; + this.value_[print_preview.ticket_items.CustomMarginsOrientation.BOTTOM] = + bottom; + this.value_[print_preview.ticket_items.CustomMarginsOrientation.LEFT] = + left; + } - /** - * Parses a margins object from the given serialized state. - * @param {Object} state Serialized representation of the margins created by - * the {@code serialize} method. - * @return {!print_preview.Margins} New margins instance. - */ - Margins.parse = function(state) { - return new print_preview.Margins( - state[print_preview.ticket_items.CustomMarginsOrientation.TOP] || 0, - state[print_preview.ticket_items.CustomMarginsOrientation.RIGHT] || 0, - state[print_preview.ticket_items.CustomMarginsOrientation.BOTTOM] || 0, - state[print_preview.ticket_items.CustomMarginsOrientation.LEFT] || 0); - }; + /** + * Parses a margins object from the given serialized state. + * @param {Object} state Serialized representation of the margins created by + * the {@code serialize} method. + * @return {!print_preview.Margins} New margins instance. + */ + static parse(state) { + return new print_preview.Margins( + state[print_preview.ticket_items.CustomMarginsOrientation.TOP] || 0, + state[print_preview.ticket_items.CustomMarginsOrientation.RIGHT] || 0, + state[print_preview.ticket_items.CustomMarginsOrientation.BOTTOM] || + 0, + state[print_preview.ticket_items.CustomMarginsOrientation.LEFT] || 0); + } - Margins.prototype = { /** * @param {!print_preview.ticket_items.CustomMarginsOrientation} * orientation Specifies the margin value to get. * @return {number} Value of the margin of the given orientation. */ - get: function(orientation) { + get(orientation) { return this.value_[orientation]; - }, + } /** * @param {!print_preview.ticket_items.CustomMarginsOrientation} @@ -61,7 +62,7 @@ cr.define('print_preview', function() { * @return {!print_preview.Margins} A new copy of |this| with the * modification made to the specified margin. */ - set: function(orientation, value) { + set(orientation, value) { var newValue = this.clone_(); newValue[orientation] = value; return new Margins( @@ -69,14 +70,14 @@ cr.define('print_preview', function() { newValue[print_preview.ticket_items.CustomMarginsOrientation.RIGHT], newValue[print_preview.ticket_items.CustomMarginsOrientation.BOTTOM], newValue[print_preview.ticket_items.CustomMarginsOrientation.LEFT]); - }, + } /** * @param {print_preview.Margins} other The other margins object to compare * against. * @return {boolean} Whether this margins object is equal to another. */ - equals: function(other) { + equals(other) { if (other == null) { return false; } @@ -86,25 +87,25 @@ cr.define('print_preview', function() { } } return true; - }, + } /** @return {Object} A serialized representation of the margins. */ - serialize: function() { + serialize() { return this.clone_(); - }, + } /** * @return {Object} Cloned state of the margins. * @private */ - clone_: function() { + clone_() { var clone = {}; for (var o in this.value_) { clone[o] = this.value_[o]; } return clone; } - }; + } // Export return {Margins: Margins}; diff --git a/chromium/chrome/browser/resources/print_preview/data/measurement_system.js b/chromium/chrome/browser/resources/print_preview/data/measurement_system.js index 01c6a55f4ee..1548de6b4e1 100644 --- a/chromium/chrome/browser/resources/print_preview/data/measurement_system.js +++ b/chromium/chrome/browser/resources/print_preview/data/measurement_system.js @@ -32,23 +32,6 @@ cr.define('print_preview', function() { } /** - * Parses |numberFormat| and extracts the symbols used for the thousands point - * and decimal point. - * @param {string} numberFormat The formatted version of the number 12345678. - * @return {!Array<string>} The extracted symbols in the order - * [thousandsSymbol, decimalSymbol]. For example, - * parseNumberFormat("123,456.78") returns [",", "."]. - */ - MeasurementSystem.parseNumberFormat = function(numberFormat) { - if (!numberFormat) { - return [',', '.']; - } - var regex = /^(\d+)(\W?)(\d+)(\W?)(\d+)$/; - var matches = numberFormat.match(regex) || ['', '', ',', '', '.']; - return [matches[2], matches[4]]; - }; - - /** * Maximum resolution of local unit values. * @type {!Object<!print_preview.MeasurementSystemUnitType, number>} * @private diff --git a/chromium/chrome/browser/resources/print_preview/data/measurement_system_unittest.gtestjs b/chromium/chrome/browser/resources/print_preview/data/measurement_system_unittest.gtestjs deleted file mode 100644 index f81960d5969..00000000000 --- a/chromium/chrome/browser/resources/print_preview/data/measurement_system_unittest.gtestjs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2012 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. - -/** - * Test fixture for the MeasurementSystem. - * @constructor - * @extends {testing.Test} - */ -function MeasurementSystemUnitTest() { - testing.Test.call(this); -} - -MeasurementSystemUnitTest.prototype = { - __proto__: testing.Test.prototype, - - extraLibraries: [ - '../../../../../ui/webui/resources/js/cr.js', - '../print_preview_utils.js', - 'measurement_system.js' - ] -}; - -TEST_F('MeasurementSystemUnitTest', 'parseNumberFormat', function() { - assertTrue(areArraysEqual( - ['.', ','], - print_preview.MeasurementSystem.parseNumberFormat('123.456,78'))); - assertTrue(areArraysEqual( - ['.', '.'], - print_preview.MeasurementSystem.parseNumberFormat('123.456.78'))); - assertTrue(areArraysEqual( - [',', '.'], - print_preview.MeasurementSystem.parseNumberFormat('123,456.78'))); - assertTrue(areArraysEqual( - [',', ','], - print_preview.MeasurementSystem.parseNumberFormat('123,456,78'))); - assertTrue(areArraysEqual( - [' ', ','], - print_preview.MeasurementSystem.parseNumberFormat('123 456,78'))); - assertTrue(areArraysEqual( - [' ', '.'], - print_preview.MeasurementSystem.parseNumberFormat('123 456.78'))); - assertTrue(areArraysEqual( - [' ', ' '], - print_preview.MeasurementSystem.parseNumberFormat('123 456 78'))); - assertTrue(areArraysEqual( - ['', ''], - print_preview.MeasurementSystem.parseNumberFormat('123'))); - - assertTrue(areArraysEqual( - [',', '.'], - print_preview.MeasurementSystem.parseNumberFormat('abcdef'))); - assertTrue(areArraysEqual( - [',', '.'], - print_preview.MeasurementSystem.parseNumberFormat(null))); - assertTrue(areArraysEqual( - [',', '.'], - print_preview.MeasurementSystem.parseNumberFormat(undefined))); - assertTrue(areArraysEqual( - [',', '.'], - print_preview.MeasurementSystem.parseNumberFormat(''))); - assertTrue(areArraysEqual( - [',', '.'], - print_preview.MeasurementSystem.parseNumberFormat('1'))); - assertTrue(areArraysEqual( - [',', '.'], - print_preview.MeasurementSystem.parseNumberFormat('12'))); -}); diff --git a/chromium/chrome/browser/resources/print_preview/data/page_number_set.js b/chromium/chrome/browser/resources/print_preview/data/page_number_set.js index 81fb7b66815..7eef255936e 100644 --- a/chromium/chrome/browser/resources/print_preview/data/page_number_set.js +++ b/chromium/chrome/browser/resources/print_preview/data/page_number_set.js @@ -5,57 +5,56 @@ cr.define('print_preview', function() { 'use strict'; - /** - * An immutable ordered set of page numbers. - * @param {!Array<number>} pageNumberList A list of page numbers to include - * in the set. - * @constructor - */ - function PageNumberSet(pageNumberList) { + class PageNumberSet { /** - * Internal data store for the page number set. - * @type {!Array<number>} - * @private + * An immutable ordered set of page numbers. + * @param {!Array<number>} pageNumberList A list of page numbers to include + * in the set. */ - this.pageNumberSet_ = pageListToPageSet(pageNumberList); - } + constructor(pageNumberList) { + /** + * Internal data store for the page number set. + * @type {!Array<number>} + * @private + */ + this.pageNumberSet_ = pageListToPageSet(pageNumberList); + } - PageNumberSet.prototype = { /** @return {number} The number of page numbers in the set. */ get size() { return this.pageNumberSet_.length; - }, + } /** * @param {number} index 0-based index of the page number to get. * @return {number} Page number at the given index. */ - getPageNumberAt: function(index) { + getPageNumberAt(index) { return this.pageNumberSet_[index]; - }, + } /** * @param {number} pageNumber 1-based page number to check for. * @return {boolean} Whether the given page number is in the page range. */ - hasPageNumber: function(pageNumber) { + hasPageNumber(pageNumber) { return arrayContains(this.pageNumberSet_, pageNumber); - }, + } /** * @param {number} pageNumber 1-based number of the page to get index of. * @return {number} 0-based index of the given page number with respect to * all of the pages in the page range. */ - getPageNumberIndex: function(pageNumber) { + getPageNumberIndex(pageNumber) { return this.pageNumberSet_.indexOf(pageNumber); - }, + } /** @return {!Array<number>} Array representation of the set. */ - asArray: function() { + asArray() { return this.pageNumberSet_.slice(0); - }, - }; + } + } // Export return {PageNumberSet: PageNumberSet}; diff --git a/chromium/chrome/browser/resources/print_preview/data/print_ticket_store.js b/chromium/chrome/browser/resources/print_preview/data/print_ticket_store.js index 63a42bc086f..6675843b53e 100644 --- a/chromium/chrome/browser/resources/print_preview/data/print_ticket_store.js +++ b/chromium/chrome/browser/resources/print_preview/data/print_ticket_store.js @@ -10,310 +10,289 @@ cr.define('print_preview', function() { // destination changes, the new destination might not support duplex anymore, // so we should clear the ticket's isDuplexEnabled state. - /** - * Storage of the print ticket and document statistics. Dispatches events when - * the contents of the print ticket or document statistics change. Also - * handles validation of the print ticket against destination capabilities and - * against the document. - * @param {!print_preview.DestinationStore} destinationStore Used to - * understand which printer is selected. - * @param {!print_preview.AppState} appState Print preview application state. - * @param {!print_preview.DocumentInfo} documentInfo Document data model. - * @constructor - * @extends {cr.EventTarget} - */ - function PrintTicketStore(destinationStore, appState, documentInfo) { - cr.EventTarget.call(this); - - /** - * Destination store used to understand which printer is selected. - * @type {!print_preview.DestinationStore} - * @private - */ - this.destinationStore_ = destinationStore; - - /** - * App state used to persist and load ticket values. - * @type {!print_preview.AppState} - * @private - */ - this.appState_ = appState; - - /** - * Information about the document to print. - * @type {!print_preview.DocumentInfo} - * @private - */ - this.documentInfo_ = documentInfo; - - /** - * Printing capabilities of Chromium and the currently selected destination. - * @type {!print_preview.CapabilitiesHolder} - * @private - */ - this.capabilitiesHolder_ = new print_preview.CapabilitiesHolder(); - - /** - * Current measurement system. Used to work with margin measurements. - * @type {!print_preview.MeasurementSystem} - * @private - */ - this.measurementSystem_ = new print_preview.MeasurementSystem( - ',', '.', print_preview.MeasurementSystemUnitType.IMPERIAL); - - /** - * Collate ticket item. - * @type {!print_preview.ticket_items.Collate} - * @private - */ - this.collate_ = new print_preview.ticket_items.Collate( - this.appState_, this.destinationStore_); - - /** - * Color ticket item. - * @type {!print_preview.ticket_items.Color} - * @private - */ - this.color_ = new print_preview.ticket_items.Color( - this.appState_, this.destinationStore_); - - /** - * Copies ticket item. - * @type {!print_preview.ticket_items.Copies} - * @private - */ - this.copies_ = - new print_preview.ticket_items.Copies(this.destinationStore_); - - /** - * DPI ticket item. - * @type {!print_preview.ticket_items.Dpi} - * @private - */ - this.dpi_ = new print_preview.ticket_items.Dpi( - this.appState_, this.destinationStore_); - - /** - * Duplex ticket item. - * @type {!print_preview.ticket_items.Duplex} - * @private - */ - this.duplex_ = new print_preview.ticket_items.Duplex( - this.appState_, this.destinationStore_); - - /** - * Page range ticket item. - * @type {!print_preview.ticket_items.PageRange} - * @private - */ - this.pageRange_ = - new print_preview.ticket_items.PageRange(this.documentInfo_); - - /** - * Rasterize PDF ticket item. - * @type {!print_preview.ticket_items.Rasterize} - * @private - */ - this.rasterize_ = new print_preview.ticket_items.Rasterize( - this.destinationStore_, this.documentInfo_); - - /** - * Scaling ticket item. - * @type {!print_preview.ticket_items.Scaling} - * @private - */ - this.scaling_ = new print_preview.ticket_items.Scaling( - this.appState_, this.destinationStore_, this.documentInfo_); - - /** - * Custom margins ticket item. - * @type {!print_preview.ticket_items.CustomMargins} - * @private - */ - this.customMargins_ = new print_preview.ticket_items.CustomMargins( - this.appState_, this.documentInfo_); - - /** - * Margins type ticket item. - * @type {!print_preview.ticket_items.MarginsType} - * @private - */ - this.marginsType_ = new print_preview.ticket_items.MarginsType( - this.appState_, this.documentInfo_, this.customMargins_); - - /** - * Media size ticket item. - * @type {!print_preview.ticket_items.MediaSize} - * @private - */ - this.mediaSize_ = new print_preview.ticket_items.MediaSize( - this.appState_, this.destinationStore_, this.documentInfo_, - this.marginsType_, this.customMargins_); - - /** - * Landscape ticket item. - * @type {!print_preview.ticket_items.Landscape} - * @private - */ - this.landscape_ = new print_preview.ticket_items.Landscape( - this.appState_, this.destinationStore_, this.documentInfo_, - this.marginsType_, this.customMargins_); - - /** - * Header-footer ticket item. - * @type {!print_preview.ticket_items.HeaderFooter} - * @private - */ - this.headerFooter_ = new print_preview.ticket_items.HeaderFooter( - this.appState_, this.documentInfo_, this.marginsType_, - this.customMargins_, this.mediaSize_, this.landscape_); - - /** - * Fit-to-page ticket item. - * @type {!print_preview.ticket_items.FitToPage} - * @private - */ - this.fitToPage_ = new print_preview.ticket_items.FitToPage( - this.appState_, this.documentInfo_, this.destinationStore_); - - /** - * Print CSS backgrounds ticket item. - * @type {!print_preview.ticket_items.CssBackground} - * @private - */ - this.cssBackground_ = new print_preview.ticket_items.CssBackground( - this.appState_, this.documentInfo_); - - /** - * Print selection only ticket item. - * @type {!print_preview.ticket_items.SelectionOnly} - * @private - */ - this.selectionOnly_ = - new print_preview.ticket_items.SelectionOnly(this.documentInfo_); + class PrintTicketStore extends cr.EventTarget { + /** + * Storage of the print ticket and document statistics. Dispatches events + * when the contents of the print ticket or document statistics change. Also + * handles validation of the print ticket against destination capabilities + * and against the document. + * @param {!print_preview.DestinationStore} destinationStore Used to + * understand which printer is selected. + * @param {!print_preview.AppState} appState Print preview application + * state. + * @param {!print_preview.DocumentInfo} documentInfo Document data model. + */ + constructor(destinationStore, appState, documentInfo) { + super(); + + /** + * Destination store used to understand which printer is selected. + * @type {!print_preview.DestinationStore} + * @private + */ + this.destinationStore_ = destinationStore; + + /** + * App state used to persist and load ticket values. + * @type {!print_preview.AppState} + * @private + */ + this.appState_ = appState; + + /** + * Information about the document to print. + * @type {!print_preview.DocumentInfo} + * @private + */ + this.documentInfo_ = documentInfo; + + /** + * Printing capabilities of Chromium and the currently selected + * destination. + * @type {!print_preview.CapabilitiesHolder} + * @private + */ + this.capabilitiesHolder_ = new print_preview.CapabilitiesHolder(); + + /** + * Current measurement system. Used to work with margin measurements. + * @type {!print_preview.MeasurementSystem} + * @private + */ + this.measurementSystem_ = new print_preview.MeasurementSystem( + ',', '.', print_preview.MeasurementSystemUnitType.IMPERIAL); + + /** + * Collate ticket item. + * @type {!print_preview.ticket_items.Collate} + * @private + */ + this.collate_ = new print_preview.ticket_items.Collate( + this.appState_, this.destinationStore_); + + /** + * Color ticket item. + * @type {!print_preview.ticket_items.Color} + * @private + */ + this.color_ = new print_preview.ticket_items.Color( + this.appState_, this.destinationStore_); + + /** + * Copies ticket item. + * @type {!print_preview.ticket_items.Copies} + * @private + */ + this.copies_ = + new print_preview.ticket_items.Copies(this.destinationStore_); + + /** + * DPI ticket item. + * @type {!print_preview.ticket_items.Dpi} + * @private + */ + this.dpi_ = new print_preview.ticket_items.Dpi( + this.appState_, this.destinationStore_); + + /** + * Duplex ticket item. + * @type {!print_preview.ticket_items.Duplex} + * @private + */ + this.duplex_ = new print_preview.ticket_items.Duplex( + this.appState_, this.destinationStore_); + + /** + * Page range ticket item. + * @type {!print_preview.ticket_items.PageRange} + * @private + */ + this.pageRange_ = + new print_preview.ticket_items.PageRange(this.documentInfo_); + + /** + * Rasterize PDF ticket item. + * @type {!print_preview.ticket_items.Rasterize} + * @private + */ + this.rasterize_ = new print_preview.ticket_items.Rasterize( + this.destinationStore_, this.documentInfo_); + + /** + * Scaling ticket item. + * @type {!print_preview.ticket_items.Scaling} + * @private + */ + this.scaling_ = new print_preview.ticket_items.Scaling( + this.appState_, this.destinationStore_, this.documentInfo_); + + /** + * Custom margins ticket item. + * @type {!print_preview.ticket_items.CustomMargins} + * @private + */ + this.customMargins_ = new print_preview.ticket_items.CustomMargins( + this.appState_, this.documentInfo_); + + /** + * Margins type ticket item. + * @type {!print_preview.ticket_items.MarginsType} + * @private + */ + this.marginsType_ = new print_preview.ticket_items.MarginsType( + this.appState_, this.documentInfo_, this.customMargins_); + + /** + * Media size ticket item. + * @type {!print_preview.ticket_items.MediaSize} + * @private + */ + this.mediaSize_ = new print_preview.ticket_items.MediaSize( + this.appState_, this.destinationStore_, this.documentInfo_, + this.marginsType_, this.customMargins_); + + /** + * Landscape ticket item. + * @type {!print_preview.ticket_items.Landscape} + * @private + */ + this.landscape_ = new print_preview.ticket_items.Landscape( + this.appState_, this.destinationStore_, this.documentInfo_, + this.marginsType_, this.customMargins_); + + /** + * Header-footer ticket item. + * @type {!print_preview.ticket_items.HeaderFooter} + * @private + */ + this.headerFooter_ = new print_preview.ticket_items.HeaderFooter( + this.appState_, this.documentInfo_, this.marginsType_, + this.customMargins_, this.mediaSize_, this.landscape_); + + /** + * Fit-to-page ticket item. + * @type {!print_preview.ticket_items.FitToPage} + * @private + */ + this.fitToPage_ = new print_preview.ticket_items.FitToPage( + this.appState_, this.documentInfo_, this.destinationStore_); + + /** + * Print CSS backgrounds ticket item. + * @type {!print_preview.ticket_items.CssBackground} + * @private + */ + this.cssBackground_ = new print_preview.ticket_items.CssBackground( + this.appState_, this.documentInfo_); + + /** + * Print selection only ticket item. + * @type {!print_preview.ticket_items.SelectionOnly} + * @private + */ + this.selectionOnly_ = + new print_preview.ticket_items.SelectionOnly(this.documentInfo_); + + /** + * Vendor ticket items. + * @type {!print_preview.ticket_items.VendorItems} + * @private + */ + this.vendorItems_ = new print_preview.ticket_items.VendorItems( + this.appState_, this.destinationStore_); + + /** + * Keeps track of event listeners for the print ticket store. + * @type {!EventTracker} + * @private + */ + this.tracker_ = new EventTracker(); + + /** + * Whether the print preview has been initialized. + * @type {boolean} + * @private + */ + this.isInitialized_ = false; + + this.addEventListeners_(); + } - /** - * Vendor ticket items. - * @type {!print_preview.ticket_items.VendorItems} - * @private - */ - this.vendorItems_ = new print_preview.ticket_items.VendorItems( - this.appState_, this.destinationStore_); - - /** - * Keeps track of event listeners for the print ticket store. - * @type {!EventTracker} - * @private - */ - this.tracker_ = new EventTracker(); - - /** - * Whether the print preview has been initialized. - * @type {boolean} - * @private - */ - this.isInitialized_ = false; - - this.addEventListeners_(); - } - - /** - * Event types dispatched by the print ticket store. - * @enum {string} - */ - PrintTicketStore.EventType = { - CAPABILITIES_CHANGE: 'print_preview.PrintTicketStore.CAPABILITIES_CHANGE', - DOCUMENT_CHANGE: 'print_preview.PrintTicketStore.DOCUMENT_CHANGE', - INITIALIZE: 'print_preview.PrintTicketStore.INITIALIZE', - TICKET_CHANGE: 'print_preview.PrintTicketStore.TICKET_CHANGE' - }; - - PrintTicketStore.prototype = { - __proto__: cr.EventTarget.prototype, - - /** - * Whether the print preview has been initialized. - * @type {boolean} - */ get isInitialized() { return this.isInitialized_; - }, + } get collate() { return this.collate_; - }, + } get color() { return this.color_; - }, + } get copies() { return this.copies_; - }, + } get cssBackground() { return this.cssBackground_; - }, + } get customMargins() { return this.customMargins_; - }, + } get dpi() { return this.dpi_; - }, + } get duplex() { return this.duplex_; - }, + } get fitToPage() { return this.fitToPage_; - }, + } get headerFooter() { return this.headerFooter_; - }, + } get mediaSize() { return this.mediaSize_; - }, + } get landscape() { return this.landscape_; - }, + } get marginsType() { return this.marginsType_; - }, + } get pageRange() { return this.pageRange_; - }, + } get rasterize() { return this.rasterize_; - }, + } get scaling() { return this.scaling_; - }, + } get selectionOnly() { return this.selectionOnly_; - }, + } get vendorItems() { return this.vendorItems_; - }, + } - /** - * @return {!print_preview.MeasurementSystem} Measurement system of the - * local system. - */ get measurementSystem() { return this.measurementSystem_; - }, + } /** * Initializes the print ticket store. Dispatches an INITIALIZE event. @@ -324,8 +303,7 @@ cr.define('print_preview', function() { * @param {boolean} selectionOnly Whether only selected content should be * printed. */ - init: function( - thousandsDelimeter, decimalDelimeter, unitType, selectionOnly) { + init(thousandsDelimeter, decimalDelimeter, unitType, selectionOnly) { this.measurementSystem_.setSystem( thousandsDelimeter, decimalDelimeter, unitType); this.selectionOnly_.updateValue(selectionOnly); @@ -404,35 +382,35 @@ cr.define('print_preview', function() { /** @type {!Object<string>} */ (this.appState_.getField( print_preview.AppStateField.VENDOR_OPTIONS))); } - }, + } /** * @return {boolean} {@code true} if the stored print ticket is valid, * {@code false} otherwise. */ - isTicketValid: function() { + isTicketValid() { return this.isTicketValidForPreview() && (!this.copies_.isCapabilityAvailable() || this.copies_.isValid()) && (!this.pageRange_.isCapabilityAvailable() || this.pageRange_.isValid()); - }, + } /** @return {boolean} Whether the ticket is valid for preview generation. */ - isTicketValidForPreview: function() { + isTicketValidForPreview() { return ( (!this.marginsType_.isCapabilityAvailable() || !this.marginsType_.isValueEqual( print_preview.ticket_items.MarginsTypeValue.CUSTOM) || this.customMargins_.isValid()) && (!this.scaling_.isCapabilityAvailable() || this.scaling_.isValid())); - }, + } /** * Creates an object that represents a Google Cloud Print print ticket. * @param {!print_preview.Destination} destination Destination to print to. * @return {string} Google Cloud Print print ticket. */ - createPrintTicket: function(destination) { + createPrintTicket(destination) { assert( !destination.isLocal || destination.isPrivet || destination.isExtension, @@ -506,18 +484,13 @@ cr.define('print_preview', function() { } } return JSON.stringify(cjt); - }, + } /** * Adds event listeners for the print ticket store. * @private */ - addEventListeners_: function() { - this.tracker_.add( - this.destinationStore_, - print_preview.DestinationStore.EventType.DESTINATION_SELECT, - this.onDestinationSelect_.bind(this)); - + addEventListeners_() { this.tracker_.add( this.destinationStore_, print_preview.DestinationStore.EventType @@ -537,14 +510,13 @@ cr.define('print_preview', function() { this.tracker_.add( this.documentInfo_, print_preview.DocumentInfo.EventType.CHANGE, this.onDocumentInfoChange_.bind(this)); - }, + } /** - * Called when the destination selected. + * Called when the capabilities of the selected destination are ready. * @private */ - onDestinationSelect_: function() { - // Reset user selection for certain ticket items. + onSelectedDestinationCapabilitiesReady_() { if (this.capabilitiesHolder_.get() != null) { this.customMargins_.updateValue(null); if (this.marginsType_.getValue() == @@ -554,13 +526,6 @@ cr.define('print_preview', function() { } this.vendorItems_.updateValue({}); } - }, - - /** - * Called when the capabilities of the selected destination are ready. - * @private - */ - onSelectedDestinationCapabilitiesReady_: function() { var caps = assert(this.destinationStore_.selectedDestination.capabilities); var isFirstUpdate = this.capabilitiesHolder_.get() == null; @@ -572,16 +537,26 @@ cr.define('print_preview', function() { cr.dispatchSimpleEvent( this, PrintTicketStore.EventType.CAPABILITIES_CHANGE); } - }, + } /** * Called when document data model has changed. Dispatches a print ticket * store event. * @private */ - onDocumentInfoChange_: function() { + onDocumentInfoChange_() { cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.DOCUMENT_CHANGE); - }, + } + } + + /** + * Event types dispatched by the print ticket store. + * @enum {string} + */ + PrintTicketStore.EventType = { + CAPABILITIES_CHANGE: 'print_preview.PrintTicketStore.CAPABILITIES_CHANGE', + DOCUMENT_CHANGE: 'print_preview.PrintTicketStore.DOCUMENT_CHANGE', + INITIALIZE: 'print_preview.PrintTicketStore.INITIALIZE', }; // Export diff --git a/chromium/chrome/browser/resources/print_preview/data/printable_area.js b/chromium/chrome/browser/resources/print_preview/data/printable_area.js index ae0f076096e..83791b717d7 100644 --- a/chromium/chrome/browser/resources/print_preview/data/printable_area.js +++ b/chromium/chrome/browser/resources/print_preview/data/printable_area.js @@ -5,56 +5,55 @@ cr.define('print_preview', function() { 'use strict'; - /** - * Object describing the printable area of a page in the document. - * @param {!print_preview.Coordinate2d} origin Top left corner of the - * printable area of the document. - * @param {!print_preview.Size} size Size of the printable area of the - * document. - * @constructor - */ - function PrintableArea(origin, size) { + class PrintableArea { /** - * Top left corner of the printable area of the document. - * @type {!print_preview.Coordinate2d} - * @private + * Object describing the printable area of a page in the document. + * @param {!print_preview.Coordinate2d} origin Top left corner of the + * printable area of the document. + * @param {!print_preview.Size} size Size of the printable area of the + * document. */ - this.origin_ = origin; + constructor(origin, size) { + /** + * Top left corner of the printable area of the document. + * @type {!print_preview.Coordinate2d} + * @private + */ + this.origin_ = origin; - /** - * Size of the printable area of the document. - * @type {!print_preview.Size} - * @private - */ - this.size_ = size; - } + /** + * Size of the printable area of the document. + * @type {!print_preview.Size} + * @private + */ + this.size_ = size; + } - PrintableArea.prototype = { /** * @return {!print_preview.Coordinate2d} Top left corner of the printable * area of the document. */ get origin() { return this.origin_; - }, + } /** * @return {!print_preview.Size} Size of the printable area of the document. */ get size() { return this.size_; - }, + } /** * @param {print_preview.PrintableArea} other Other printable area to check * for equality. * @return {boolean} Whether another printable area is equal to this one. */ - equals: function(other) { + equals(other) { return other != null && this.origin_.equals(other.origin_) && this.size_.equals(other.size_); } - }; + } // Export return {PrintableArea: PrintableArea}; diff --git a/chromium/chrome/browser/resources/print_preview/data/size.js b/chromium/chrome/browser/resources/print_preview/data/size.js index 078ce7d7b1e..8012258284d 100644 --- a/chromium/chrome/browser/resources/print_preview/data/size.js +++ b/chromium/chrome/browser/resources/print_preview/data/size.js @@ -5,48 +5,47 @@ cr.define('print_preview', function() { 'use strict'; - /** - * Immutable two-dimensional size. - * @param {number} width Width of the size. - * @param {number} height Height of the size. - * @constructor - */ - function Size(width, height) { + class Size { /** - * Width of the size. - * @type {number} - * @private + * Immutable two-dimensional size. + * @param {number} width Width of the size. + * @param {number} height Height of the size. */ - this.width_ = width; + constructor(width, height) { + /** + * Width of the size. + * @type {number} + * @private + */ + this.width_ = width; - /** - * Height of the size. - * @type {number} - * @private - */ - this.height_ = height; - } + /** + * Height of the size. + * @type {number} + * @private + */ + this.height_ = height; + } - Size.prototype = { /** @return {number} Width of the size. */ get width() { return this.width_; - }, + } /** @return {number} Height of the size. */ get height() { return this.height_; - }, + } /** * @param {print_preview.Size} other Other size object to compare against. * @return {boolean} Whether this size object is equal to another. */ - equals: function(other) { + equals(other) { return other != null && this.width_ == other.width_ && this.height_ == other.height_; } - }; + } // Export return {Size: Size}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/collate.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/collate.js index ec59d895da3..227cf03cf2d 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/collate.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/collate.js @@ -5,57 +5,53 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Collate ticket item whose value is a {@code boolean} that indicates whether - * collation is enabled. - * @param {!print_preview.AppState} appState App state used to persist collate - * selection. - * @param {!print_preview.DestinationStore} destinationStore Destination store - * used determine if a destination has the collate capability. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function Collate(appState, destinationStore) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.IS_COLLATE_ENABLED, - destinationStore); - } - - Collate.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class Collate extends print_preview.ticket_items.TicketItem { + /** + * Collate ticket item whose value is a {@code boolean} that indicates + * whether collation is enabled. + * @param {!print_preview.AppState} appState App state used to persist + * collate selection. + * @param {!print_preview.DestinationStore} destinationStore Destination + * store used determine if a destination has the collate capability. + */ + constructor(appState, destinationStore) { + super( + appState, print_preview.AppStateField.IS_COLLATE_ENABLED, + destinationStore); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return !!this.getCollateCapability_(); - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { var capability = this.getCollateCapability_(); return capability.hasOwnProperty('default') ? capability.default : true; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return true; - }, + } /** * @return {Object} Collate capability of the selected destination. * @private */ - getCollateCapability_: function() { + getCollateCapability_() { var dest = this.getSelectedDestInternal(); return (dest && dest.capabilities && dest.capabilities.printer && dest.capabilities.printer.collate) || null; } - }; + } // Export return {Collate: Collate}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/color.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/color.js index 67d2dec3619..a78d6ccc9e1 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/color.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/color.js @@ -5,44 +5,28 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Color ticket item whose value is a {@code boolean} that indicates whether - * the document should be printed in color. - * @param {!print_preview.AppState} appState App state persistence object to - * save the state of the color selection. - * @param {!print_preview.DestinationStore} destinationStore Used to determine - * whether color printing should be available. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function Color(appState, destinationStore) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.IS_COLOR_ENABLED, - destinationStore); - } - - /** - * @private {!Array<string>} List of capability types considered color. - * @const - */ - Color.COLOR_TYPES_ = ['STANDARD_COLOR', 'CUSTOM_COLOR']; - - /** - * @private {!Array<string>} List of capability types considered monochrome. - * @const - */ - Color.MONOCHROME_TYPES_ = ['STANDARD_MONOCHROME', 'CUSTOM_MONOCHROME']; - - Color.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class Color extends print_preview.ticket_items.TicketItem { + /** + * Color ticket item whose value is a {@code boolean} that indicates whether + * the document should be printed in color. + * @param {!print_preview.AppState} appState App state persistence object to + * save the state of the color selection. + * @param {!print_preview.DestinationStore} destinationStore Used to + * determine whether color printing should be available. + */ + constructor(appState, destinationStore) { + super( + appState, print_preview.AppStateField.IS_COLOR_ENABLED, + destinationStore); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { var capability = this.capability; if (!capability) { return false; @@ -55,7 +39,7 @@ cr.define('print_preview.ticket_items', function() { (Color.MONOCHROME_TYPES_.indexOf(option.type) >= 0); }); return hasColor && hasMonochrome; - }, + } /** @return {Object} Color capability of the selected destination. */ get capability() { @@ -63,10 +47,10 @@ cr.define('print_preview.ticket_items', function() { return (dest && dest.capabilities && dest.capabilities.printer && dest.capabilities.printer.color) || null; - }, + } /** @return {Object} Color option corresponding to the current value. */ - getSelectedOption: function() { + getSelectedOption() { var capability = this.capability; var options = capability ? capability.option : null; if (options) { @@ -82,19 +66,19 @@ cr.define('print_preview.ticket_items', function() { } } return null; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { var capability = this.capability; var defaultOption = capability ? this.getDefaultColorOption_(capability.option) : null; return defaultOption && (Color.COLOR_TYPES_.indexOf(defaultOption.type) >= 0); - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { // TODO(rltoscano): Get rid of this check based on destination ID. These // destinations should really update their CDDs to have only one color // option that has type 'STANDARD_COLOR'. @@ -106,7 +90,7 @@ cr.define('print_preview.ticket_items', function() { } } return this.getDefaultValueInternal(); - }, + } /** * @param {!Array<!Object<{type: (string|undefined), @@ -116,13 +100,26 @@ cr.define('print_preview.ticket_items', function() { * option of the given list. * @private */ - getDefaultColorOption_: function(options) { + getDefaultColorOption_(options) { var defaultOptions = options.filter(function(option) { return option.is_default; }); return (defaultOptions.length == 0) ? null : defaultOptions[0]; } - }; + } + + /** + * @private {!Array<string>} List of capability types considered color. + * @const + */ + Color.COLOR_TYPES_ = ['STANDARD_COLOR', 'CUSTOM_COLOR']; + + /** + * @private {!Array<string>} List of capability types considered monochrome. + * @const + */ + Color.MONOCHROME_TYPES_ = ['STANDARD_MONOCHROME', 'CUSTOM_MONOCHROME']; + // Export return {Color: Color}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/copies.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/copies.js index 936342cb1d2..84f20a7cb2f 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/copies.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/copies.js @@ -5,61 +5,56 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Copies ticket item whose value is a {@code string} that indicates how many - * copies of the document should be printed. The ticket item is backed by a - * string since the user can textually input the copies value. - * @param {!print_preview.DestinationStore} destinationStore Destination store - * used determine if a destination has the copies capability. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function Copies(destinationStore) { - print_preview.ticket_items.TicketItem.call( - this, null /*appState*/, null /*field*/, destinationStore); - } - - Copies.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class Copies extends print_preview.ticket_items.TicketItem { + /** + * Copies ticket item whose value is a {@code string} that indicates how + * many copies of the document should be printed. The ticket item is backed + * by a string since the user can textually input the copies value. + * @param {!print_preview.DestinationStore} destinationStore Destination + * store used to determine if a destination has the copies capability. + */ + constructor(destinationStore) { + super(null /*appState*/, null /*field*/, destinationStore); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return value != ''; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return !!this.getCopiesCapability_(); - }, + } /** @return {number} The number of copies indicated by the ticket item. */ - getValueAsNumber: function() { + getValueAsNumber() { var value = this.getValue(); return value == '' ? 0 : parseInt(value, 10); - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { var cap = this.getCopiesCapability_(); return cap.hasOwnProperty('default') ? cap.default : '1'; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return '1'; - }, + } /** * @return {Object} Copies capability of the selected destination. * @private */ - getCopiesCapability_: function() { + getCopiesCapability_() { var dest = this.getSelectedDestInternal(); return (dest && dest.capabilities && dest.capabilities.printer && dest.capabilities.printer.copies) || null; } - }; + } // Export return {Copies: Copies}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/css_background.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/css_background.js index ab205878a7d..9279d03ec0f 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/css_background.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/css_background.js @@ -5,45 +5,41 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Ticket item whose value is a {@code boolean} that represents whether to - * print CSS backgrounds. - * @param {!print_preview.AppState} appState App state to persist CSS - * background value. - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function CssBackground(appState, documentInfo) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.IS_CSS_BACKGROUND_ENABLED, - null /*destinationStore*/, documentInfo); - } - - CssBackground.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class CssBackground extends print_preview.ticket_items.TicketItem { + /** + * Ticket item whose value is a {@code boolean} that represents whether to + * print CSS backgrounds. + * @param {!print_preview.AppState} appState App state to persist CSS + * background value. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + */ + constructor(appState, documentInfo) { + super( + appState, print_preview.AppStateField.IS_CSS_BACKGROUND_ENABLED, + null /*destinationStore*/, documentInfo); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return this.getDocumentInfoInternal().isModifiable; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { return false; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return false; } - }; + } // Export return {CssBackground: CssBackground}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/custom_margins.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/custom_margins.js index 97c0ed8399b..a29bc892378 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/custom_margins.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/custom_margins.js @@ -21,51 +21,23 @@ cr.define('print_preview.ticket_items', function() { var CustomMarginsOrientation = print_preview.ticket_items.CustomMarginsOrientation; - /** - * Custom page margins ticket item whose value is a - * {@code print_preview.Margins}. - * @param {!print_preview.AppState} appState App state used to persist custom - * margins. - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function CustomMargins(appState, documentInfo) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.CUSTOM_MARGINS, - null /*destinationStore*/, documentInfo); - } - - /** - * Mapping of a margin orientation to its opposite. - * @type {!Object<!print_preview.ticket_items.CustomMarginsOrientation, - * !print_preview.ticket_items.CustomMarginsOrientation>} - * @private - */ - CustomMargins.OppositeOrientation_ = {}; - CustomMargins.OppositeOrientation_[CustomMarginsOrientation.TOP] = - CustomMarginsOrientation.BOTTOM; - CustomMargins.OppositeOrientation_[CustomMarginsOrientation.RIGHT] = - CustomMarginsOrientation.LEFT; - CustomMargins.OppositeOrientation_[CustomMarginsOrientation.BOTTOM] = - CustomMarginsOrientation.TOP; - CustomMargins.OppositeOrientation_[CustomMarginsOrientation.LEFT] = - CustomMarginsOrientation.RIGHT; - - /** - * Minimum distance in points that two margins can be separated by. - * @type {number} - * @const - * @private - */ - CustomMargins.MINIMUM_MARGINS_DISTANCE_ = 72; // 1 inch. - - CustomMargins.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class CustomMargins extends print_preview.ticket_items.TicketItem { + /** + * Custom page margins ticket item whose value is a + * {@code print_preview.Margins}. + * @param {!print_preview.AppState} appState App state used to persist + * custom margins. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + */ + constructor(appState, documentInfo) { + super( + appState, print_preview.AppStateField.CUSTOM_MARGINS, + null /*destinationStore*/, documentInfo); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { var margins = /** @type {!print_preview.Margins} */ (value); for (var key in CustomMarginsOrientation) { var o = CustomMarginsOrientation[key]; @@ -76,31 +48,31 @@ cr.define('print_preview.ticket_items', function() { } } return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return this.getDocumentInfoInternal().isModifiable; - }, + } /** @override */ - isValueEqual: function(value) { + isValueEqual(value) { return this.getValue().equals(value); - }, + } /** * @param {!print_preview.ticket_items.CustomMarginsOrientation} * orientation Specifies the margin to get the maximum value for. * @return {number} Maximum value in points of the specified margin. */ - getMarginMax: function(orientation) { + getMarginMax(orientation) { var oppositeOrient = CustomMargins.OppositeOrientation_[orientation]; var margins = /** @type {!print_preview.Margins} */ (this.getValue()); return this.getMarginMax_(orientation, margins.get(oppositeOrient)); - }, + } /** @override */ - updateValue: function(value) { + updateValue(value) { var margins = /** @type {!print_preview.Margins} */ (value); if (margins != null) { margins = new print_preview.Margins( @@ -111,7 +83,7 @@ cr.define('print_preview.ticket_items', function() { } print_preview.ticket_items.TicketItem.prototype.updateValue.call( this, margins); - }, + } /** * Updates the specified margin in points while keeping the value within @@ -120,26 +92,26 @@ cr.define('print_preview.ticket_items', function() { * orientation Specifies the margin to update. * @param {number} value Updated margin value in points. */ - updateMargin: function(orientation, value) { + updateMargin(orientation, value) { var margins = /** @type {!print_preview.Margins} */ (this.getValue()); var oppositeOrientation = CustomMargins.OppositeOrientation_[orientation]; var max = this.getMarginMax_(orientation, margins.get(oppositeOrientation)); value = Math.max(0, Math.min(max, value)); this.updateValue(margins.set(orientation, value)); - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { return this.getDocumentInfoInternal().margins || new print_preview.Margins(72, 72, 72, 72); - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return this.getDocumentInfoInternal().margins || new print_preview.Margins(72, 72, 72, 72); - }, + } /** * @param {!print_preview.ticket_items.CustomMarginsOrientation} @@ -149,7 +121,7 @@ cr.define('print_preview.ticket_items', function() { * @return {number} Maximum value in points of the specified margin. * @private */ - getMarginMax_: function(orientation, oppositeMargin) { + getMarginMax_(orientation, oppositeMargin) { var dimensionLength = (orientation == CustomMarginsOrientation.TOP || orientation == CustomMarginsOrientation.BOTTOM) ? this.getDocumentInfoInternal().pageSize.height : @@ -159,7 +131,32 @@ cr.define('print_preview.ticket_items', function() { dimensionLength - CustomMargins.MINIMUM_MARGINS_DISTANCE_; return Math.round(totalMargin > 0 ? totalMargin - oppositeMargin : 0); } - }; + } + + /** + * Mapping of a margin orientation to its opposite. + * @type {!Object<!print_preview.ticket_items.CustomMarginsOrientation, + * !print_preview.ticket_items.CustomMarginsOrientation>} + * @private + */ + CustomMargins.OppositeOrientation_ = {}; + CustomMargins.OppositeOrientation_[CustomMarginsOrientation.TOP] = + CustomMarginsOrientation.BOTTOM; + CustomMargins.OppositeOrientation_[CustomMarginsOrientation.RIGHT] = + CustomMarginsOrientation.LEFT; + CustomMargins.OppositeOrientation_[CustomMarginsOrientation.BOTTOM] = + CustomMarginsOrientation.TOP; + CustomMargins.OppositeOrientation_[CustomMarginsOrientation.LEFT] = + CustomMarginsOrientation.RIGHT; + + /** + * Minimum distance in points that two margins can be separated by. + * @type {number} + * @const + * @private + */ + CustomMargins.MINIMUM_MARGINS_DISTANCE_ = 72; // 1 inch. + // Export return {CustomMargins: CustomMargins}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/dpi.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/dpi.js index 7a514f1c9d8..218c7cc9b8f 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/dpi.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/dpi.js @@ -5,25 +5,20 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * DPI ticket item. - * @param {!print_preview.AppState} appState App state used to persist DPI - * selection. - * @param {!print_preview.DestinationStore} destinationStore Destination store - * used to determine if a destination has the DPI capability. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function Dpi(appState, destinationStore) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.DPI, destinationStore); - } - - Dpi.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class Dpi extends print_preview.ticket_items.TicketItem { + /** + * DPI ticket item. + * @param {!print_preview.AppState} appState App state used to persist DPI + * selection. + * @param {!print_preview.DestinationStore} destinationStore Destination + * store used to determine if a destination has the DPI capability. + */ + constructor(appState, destinationStore) { + super(appState, print_preview.AppStateField.DPI, destinationStore); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { if (!this.isCapabilityAvailable()) return false; return this.capability.option.some(function(option) { @@ -31,21 +26,21 @@ cr.define('print_preview.ticket_items', function() { option.vertical_dpi == value.vertical_dpi && option.vendor_id == value.vendor_id; }); - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return !!this.capability && !!this.capability.option && this.capability.option.length > 1; - }, + } /** @override */ - isValueEqual: function(value) { + isValueEqual(value) { var myValue = this.getValue(); return myValue.horizontal_dpi == value.horizontal_dpi && myValue.vertical_dpi == value.vertical_dpi && myValue.vendor_id == value.vendor_id; - }, + } /** @return {Object} DPI capability of the selected destination. */ get capability() { @@ -54,21 +49,21 @@ cr.define('print_preview.ticket_items', function() { destination.capabilities.printer && destination.capabilities.printer.dpi) || null; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { var defaultOptions = this.capability.option.filter(function(option) { return option.is_default; }); return defaultOptions.length > 0 ? defaultOptions[0] : null; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return {}; } - }; + } // Export return {Dpi: Dpi}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/duplex.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/duplex.js index 9930e1cac0b..6d061d70360 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/duplex.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/duplex.js @@ -5,32 +5,28 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Duplex ticket item whose value is a {@code boolean} that indicates whether - * the document should be duplex printed. - * @param {!print_preview.AppState} appState App state used to persist collate - * selection. - * @param {!print_preview.DestinationStore} destinationStore Destination store - * used determine if a destination has the collate capability. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function Duplex(appState, destinationStore) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.IS_DUPLEX_ENABLED, - destinationStore); - } - - Duplex.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class Duplex extends print_preview.ticket_items.TicketItem { + /** + * Duplex ticket item whose value is a {@code boolean} that indicates + * whether the document should be duplex printed. + * @param {!print_preview.AppState} appState App state used to persist + * collate selection. + * @param {!print_preview.DestinationStore} destinationStore Destination + * store used determine if a destination has the collate capability. + */ + constructor(appState, destinationStore) { + super( + appState, print_preview.AppStateField.IS_DUPLEX_ENABLED, + destinationStore); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { var cap = this.getDuplexCapability_(); if (!cap) { return false; @@ -42,34 +38,34 @@ cr.define('print_preview.ticket_items', function() { hasSimplexOption = hasSimplexOption || option.type == 'NO_DUPLEX'; }); return hasLongEdgeOption && hasSimplexOption; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { var cap = this.getDuplexCapability_(); var defaultOptions = cap.option.filter(function(option) { return option.is_default; }); return defaultOptions.length == 0 ? false : defaultOptions[0].type == 'LONG_EDGE'; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return false; - }, + } /** * @return {Object} Duplex capability of the selected destination. * @private */ - getDuplexCapability_: function() { + getDuplexCapability_() { var dest = this.getSelectedDestInternal(); return (dest && dest.capabilities && dest.capabilities.printer && dest.capabilities.printer.duplex) || null; } - }; + } // Export return {Duplex: Duplex}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/fit_to_page.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/fit_to_page.js index 6dc52f782e9..e24c6fd11d2 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/fit_to_page.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/fit_to_page.js @@ -5,54 +5,51 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Fit-to-page ticket item whose value is a {@code boolean} that indicates - * whether to scale the document to fit the page. - * @param {!print_preview.AppState} appState App state to persist item value. - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print. - * @param {!print_preview.DestinationStore} destinationStore Used to determine - * whether fit to page should be available. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function FitToPage(appState, documentInfo, destinationStore) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.IS_FIT_TO_PAGE_ENABLED, - destinationStore, documentInfo); - } - - FitToPage.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class FitToPage extends print_preview.ticket_items.TicketItem { + /** + * Fit-to-page ticket item whose value is a {@code boolean} that indicates + * whether to scale the document to fit the page. + * @param {!print_preview.AppState} appState App state to persist item + * value. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @param {!print_preview.DestinationStore} destinationStore Used to + * determine whether fit to page should be available. + */ + constructor(appState, documentInfo, destinationStore) { + super( + appState, print_preview.AppStateField.IS_FIT_TO_PAGE_ENABLED, + destinationStore, documentInfo); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return !this.getDocumentInfoInternal().isModifiable && (!this.getSelectedDestInternal() || this.getSelectedDestInternal().id != print_preview.Destination.GooglePromotedId.SAVE_AS_PDF); - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { // It's on by default since it is not a document feature, it is rather // a property of the printer, hardware margins limitations. User can // always override it. return true; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return !this.getSelectedDestInternal() || this.getSelectedDestInternal().id != print_preview.Destination.GooglePromotedId.SAVE_AS_PDF; } - }; + } // Export return {FitToPage: FitToPage}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/header_footer.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/header_footer.js index b144537bbd4..f69389f27c9 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/header_footer.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/header_footer.js @@ -5,77 +5,64 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Header-footer ticket item whose value is a {@code boolean} that indicates - * whether the document should be printed with headers and footers. - * @param {!print_preview.AppState} appState App state used to persist whether - * header-footer is enabled. - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print. - * @param {!print_preview.ticket_items.MarginsType} marginsType Ticket item - * that stores which predefined margins to print with. - * @param {!print_preview.ticket_items.CustomMargins} customMargins Ticket - * item that stores custom margin values. - * @param {!print_preview.ticket_items.MediaSize} mediaSize Ticket item that - * stores media size values. - * @param {!print_preview.ticket_items.Landscape} landscape Ticket item that - * stores landscape values. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function HeaderFooter( - appState, documentInfo, marginsType, customMargins, mediaSize, - landscape) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.IS_HEADER_FOOTER_ENABLED, - null /*destinationStore*/, documentInfo); - + class HeaderFooter extends print_preview.ticket_items.TicketItem { /** - * Ticket item that stores which predefined margins to print with. - * @private {!print_preview.ticket_items.MarginsType} + * Header-footer ticket item whose value is a {@code boolean} that indicates + * whether the document should be printed with headers and footers. + * @param {!print_preview.AppState} appState App state used to persist + * whether header-footer is enabled. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @param {!print_preview.ticket_items.MarginsType} marginsType Ticket item + * that stores which predefined margins to print with. + * @param {!print_preview.ticket_items.CustomMargins} customMargins Ticket + * item that stores custom margin values. + * @param {!print_preview.ticket_items.MediaSize} mediaSize Ticket item that + * stores media size values. + * @param {!print_preview.ticket_items.Landscape} landscape Ticket item that + * stores landscape values. */ - this.marginsType_ = marginsType; + constructor( + appState, documentInfo, marginsType, customMargins, mediaSize, + landscape) { + super( + appState, print_preview.AppStateField.IS_HEADER_FOOTER_ENABLED, + null /*destinationStore*/, documentInfo); - /** - * Ticket item that stores custom margin values. - * @private {!print_preview.ticket_items.CustomMargins} - */ - this.customMargins_ = customMargins; + /** + * Ticket item that stores which predefined margins to print with. + * @private {!print_preview.ticket_items.MarginsType} + */ + this.marginsType_ = marginsType; - /** - * Ticket item that stores media size values. - * @private {!print_preview.ticket_items.MediaSize} - */ - this.mediaSize_ = mediaSize; + /** + * Ticket item that stores custom margin values. + * @private {!print_preview.ticket_items.CustomMargins} + */ + this.customMargins_ = customMargins; - /** - * Ticket item that stores landscape values. - * @private {!print_preview.ticket_items.Landscape} - */ - this.landscape_ = landscape; + /** + * Ticket item that stores media size values. + * @private {!print_preview.ticket_items.MediaSize} + */ + this.mediaSize_ = mediaSize; - this.addEventListeners_(); - } + /** + * Ticket item that stores landscape values. + * @private {!print_preview.ticket_items.Landscape} + */ + this.landscape_ = landscape; - /** - * Minimum height of page in microns to allow headers and footers. Should - * match the value for min_size_printer_units in printing/print_settings.cc - * so that we do not request header/footer for margins that will be zero. - * @private {number} - * @const - */ - HeaderFooter.MINIMUM_HEIGHT_MICRONS_ = 25400; - - HeaderFooter.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + this.addEventListeners_(); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { if (!this.getDocumentInfoInternal().isModifiable) { return false; } @@ -108,23 +95,23 @@ cr.define('print_preview.ticket_items', function() { var orientEnum = print_preview.ticket_items.CustomMarginsOrientation; return margins == null || margins.get(orientEnum.TOP) > 0 || margins.get(orientEnum.BOTTOM) > 0; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { return true; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return false; - }, + } /** * Adds CHANGE listeners to dependent ticket items. * @private */ - addEventListeners_: function() { + addEventListeners_() { this.getTrackerInternal().add( this.marginsType_, print_preview.ticket_items.TicketItem.EventType.CHANGE, @@ -142,7 +129,17 @@ cr.define('print_preview.ticket_items', function() { print_preview.ticket_items.TicketItem.EventType.CHANGE, this.dispatchChangeEventInternal.bind(this)); } - }; + } + + /** + * Minimum height of page in microns to allow headers and footers. Should + * match the value for min_size_printer_units in printing/print_settings.cc + * so that we do not request header/footer for margins that will be zero. + * @private {number} + * @const + */ + HeaderFooter.MINIMUM_HEIGHT_MICRONS_ = 25400; + // Export return {HeaderFooter: HeaderFooter}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/landscape.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/landscape.js index df4dc00d0e8..5c8ef4f6438 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/landscape.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/landscape.js @@ -5,54 +5,50 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Landscape ticket item whose value is a {@code boolean} that indicates - * whether the document should be printed in landscape orientation. - * @param {!print_preview.AppState} appState App state object used to persist - * ticket item values. - * @param {!print_preview.DestinationStore} destinationStore Destination store - * used to determine the default landscape value and if landscape - * printing is available. - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print. - * @param {!print_preview.ticket_items.MarginsType} marginsType Reset when - * landscape value changes. - * @param {!print_preview.ticket_items.CustomMargins} customMargins Reset when - * landscape value changes. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function Landscape( - appState, destinationStore, documentInfo, marginsType, customMargins) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.IS_LANDSCAPE_ENABLED, - destinationStore, documentInfo); - + class Landscape extends print_preview.ticket_items.TicketItem { /** - * Margins ticket item. Reset when landscape ticket item changes. - * @type {!print_preview.ticket_items.MarginsType} - * @private + * Landscape ticket item whose value is a {@code boolean} that indicates + * whether the document should be printed in landscape orientation. + * @param {!print_preview.AppState} appState App state object used to + * persist ticket item values. + * @param {!print_preview.DestinationStore} destinationStore Destination + * store used to determine the default landscape value and if landscape + * printing is available. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @param {!print_preview.ticket_items.MarginsType} marginsType Reset when + * landscape value changes. + * @param {!print_preview.ticket_items.CustomMargins} customMargins Reset + * when landscape value changes. */ - this.marginsType_ = marginsType; + constructor( + appState, destinationStore, documentInfo, marginsType, customMargins) { + super( + appState, print_preview.AppStateField.IS_LANDSCAPE_ENABLED, + destinationStore, documentInfo); - /** - * Custom margins ticket item. Reset when landscape ticket item changes. - * @type {!print_preview.ticket_items.CustomMargins} - * @private - */ - this.customMargins_ = customMargins; - } + /** + * Margins ticket item. Reset when landscape ticket item changes. + * @type {!print_preview.ticket_items.MarginsType} + * @private + */ + this.marginsType_ = marginsType; - Landscape.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + /** + * Custom margins ticket item. Reset when landscape ticket item changes. + * @type {!print_preview.ticket_items.CustomMargins} + * @private + */ + this.customMargins_ = customMargins; + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { var cap = this.getPageOrientationCapability_(); if (!cap) return false; @@ -70,28 +66,28 @@ cr.define('print_preview.ticket_items', function() { return this.getDocumentInfoInternal().isModifiable && !this.getDocumentInfoInternal().hasCssMediaStyles && hasAutoOrPortraitOption && hasLandscapeOption; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { var cap = this.getPageOrientationCapability_(); var defaultOptions = cap.option.filter(function(option) { return option.is_default; }); return defaultOptions.length == 0 ? false : defaultOptions[0].type == 'LANDSCAPE'; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { var doc = this.getDocumentInfoInternal(); return doc.hasCssMediaStyles ? (doc.pageSize.width > doc.pageSize.height) : false; - }, + } /** @override */ - updateValueInternal: function(value) { + updateValueInternal(value) { var updateMargins = !this.isValueEqual(value); print_preview.ticket_items.TicketItem.prototype.updateValueInternal.call( this, value); @@ -101,32 +97,32 @@ cr.define('print_preview.ticket_items', function() { print_preview.ticket_items.MarginsTypeValue.DEFAULT); this.customMargins_.updateValue(null); } - }, + } /** * @return {boolean} Whether capability contains the |value|. * @param {string} value Option to check. */ - hasOption: function(value) { + hasOption(value) { var cap = this.getPageOrientationCapability_(); if (!cap) return false; return cap.option.some(function(option) { return option.type == value; }); - }, + } /** * @return {Object} Page orientation capability of the selected destination. * @private */ - getPageOrientationCapability_: function() { + getPageOrientationCapability_() { var dest = this.getSelectedDestInternal(); return (dest && dest.capabilities && dest.capabilities.printer && dest.capabilities.printer.page_orientation) || null; } - }; + } // Export return {Landscape: Landscape}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/margins_type.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/margins_type.js index 808e1486464..a9ae4351994 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/margins_type.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/margins_type.js @@ -20,57 +20,53 @@ print_preview.ticket_items.MarginsTypeValue = { cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Margins type ticket item whose value is a - * print_preview.ticket_items.MarginsTypeValue} that indicates what - * predefined margins type to use. - * @param {!print_preview.AppState} appState App state persistence object to - * save the state of the margins type selection. - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print. - * @param {!print_preview.ticket_items.CustomMargins} customMargins Custom - * margins ticket item, used to write when margins type changes. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function MarginsType(appState, documentInfo, customMargins) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.MARGINS_TYPE, - null /*destinationStore*/, documentInfo); - + class MarginsType extends print_preview.ticket_items.TicketItem { /** - * Custom margins ticket item, used to write when margins type changes. - * @type {!print_preview.ticket_items.CustomMargins} - * @private + * Margins type ticket item whose value is a + * print_preview.ticket_items.MarginsTypeValue} that indicates what + * predefined margins type to use. + * @param {!print_preview.AppState} appState App state persistence object to + * save the state of the margins type selection. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @param {!print_preview.ticket_items.CustomMargins} customMargins Custom + * margins ticket item, used to write when margins type changes. */ - this.customMargins_ = customMargins; - } + constructor(appState, documentInfo, customMargins) { + super( + appState, print_preview.AppStateField.MARGINS_TYPE, + null /*destinationStore*/, documentInfo); - MarginsType.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + /** + * Custom margins ticket item, used to write when margins type changes. + * @type {!print_preview.ticket_items.CustomMargins} + * @private + */ + this.customMargins_ = customMargins; + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return this.getDocumentInfoInternal().isModifiable; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { return print_preview.ticket_items.MarginsTypeValue.DEFAULT; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return print_preview.ticket_items.MarginsTypeValue.DEFAULT; - }, + } /** @override */ - updateValueInternal: function(value) { + updateValueInternal(value) { print_preview.ticket_items.TicketItem.prototype.updateValueInternal.call( this, value); if (this.isValueEqual( @@ -80,7 +76,7 @@ cr.define('print_preview.ticket_items', function() { this.customMargins_.updateValue(this.customMargins_.getValue()); } } - }; + } // Export return {MarginsType: MarginsType}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/media_size.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/media_size.js index 09abc7a3c91..8f49b7eaaba 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/media_size.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/media_size.js @@ -5,45 +5,42 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Media size ticket item. - * @param {!print_preview.AppState} appState App state used to persist media - * size selection. - * @param {!print_preview.DestinationStore} destinationStore Destination store - * used to determine if a destination has the media size capability. - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print. - * @param {!print_preview.ticket_items.MarginsType} marginsType Reset when - * landscape value changes. - * @param {!print_preview.ticket_items.CustomMargins} customMargins Reset when - * landscape value changes. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function MediaSize( - appState, destinationStore, documentInfo, marginsType, customMargins) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.MEDIA_SIZE, - destinationStore, documentInfo); - + class MediaSize extends print_preview.ticket_items.TicketItem { /** - * Margins ticket item. Reset when this item changes. - * @private {!print_preview.ticket_items.MarginsType} + * Media size ticket item. + * @param {!print_preview.AppState} appState App state used to persist media + * size selection. + * @param {!print_preview.DestinationStore} destinationStore Destination + * store used to determine if a destination has the media size + * capability. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @param {!print_preview.ticket_items.MarginsType} marginsType Reset when + * landscape value changes. + * @param {!print_preview.ticket_items.CustomMargins} customMargins Reset + * when landscape value changes. */ - this.marginsType_ = marginsType; + constructor( + appState, destinationStore, documentInfo, marginsType, customMargins) { + super( + appState, print_preview.AppStateField.MEDIA_SIZE, destinationStore, + documentInfo); - /** - * Custom margins ticket item. Reset when this item changes. - * @private {!print_preview.ticket_items.CustomMargins} - */ - this.customMargins_ = customMargins; - } + /** + * Margins ticket item. Reset when this item changes. + * @private {!print_preview.ticket_items.MarginsType} + */ + this.marginsType_ = marginsType; - MediaSize.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + /** + * Custom margins ticket item. Reset when this item changes. + * @private {!print_preview.ticket_items.CustomMargins} + */ + this.customMargins_ = customMargins; + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { if (!this.isCapabilityAvailable()) { return false; } @@ -53,10 +50,10 @@ cr.define('print_preview.ticket_items', function() { option.is_continuous_feed == value.is_continuous_feed && option.vendor_id == value.vendor_id; }); - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { var knownSizeToSaveAsPdf = (!this.getDocumentInfoInternal().isModifiable || this.getDocumentInfoInternal().hasCssMediaStyles) && @@ -64,16 +61,16 @@ cr.define('print_preview.ticket_items', function() { this.getSelectedDestInternal().id == print_preview.Destination.GooglePromotedId.SAVE_AS_PDF; return !knownSizeToSaveAsPdf && !!this.capability; - }, + } /** @override */ - isValueEqual: function(value) { + isValueEqual(value) { var myValue = this.getValue(); return myValue.width_microns == value.width_microns && myValue.height_microns == value.height_microns && myValue.is_continuous_feed == value.is_continuous_feed && myValue.vendor_id == value.vendor_id; - }, + } /** @return {Object} Media size capability of the selected destination. */ get capability() { @@ -82,23 +79,23 @@ cr.define('print_preview.ticket_items', function() { destination.capabilities.printer && destination.capabilities.printer.media_size) || null; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { var defaultOptions = this.capability.option.filter(function(option) { return option.is_default; }); return defaultOptions.length > 0 ? defaultOptions[0] : null; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return {}; - }, + } /** @override */ - updateValueInternal: function(value) { + updateValueInternal(value) { var updateMargins = !this.isValueEqual(value); print_preview.ticket_items.TicketItem.prototype.updateValueInternal.call( this, value); @@ -109,7 +106,7 @@ cr.define('print_preview.ticket_items', function() { this.customMargins_.updateValue(null); } } - }; + } // Export return {MediaSize: MediaSize}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/page_range.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/page_range.js index 5eb176f8bbe..23de286cfe4 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/page_range.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/page_range.js @@ -5,79 +5,67 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Page range ticket item whose value is a {@code string} that represents - * which pages in the document should be printed. - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function PageRange(documentInfo) { - print_preview.ticket_items.TicketItem.call( - this, null /*appState*/, null /*field*/, null /*destinationStore*/, - documentInfo); - } - - /** - * Impossibly large page number. - * @type {number} - * @const - * @private - */ - PageRange.MAX_PAGE_NUMBER_ = 1000000000; - - PageRange.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class PageRange extends print_preview.ticket_items.TicketItem { + /** + * Page range ticket item whose value is a {@code string} that represents + * which pages in the document should be printed. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + */ + constructor(documentInfo) { + super( + null /*appState*/, null /*field*/, null /*destinationStore*/, + documentInfo); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { var result = pageRangeTextToPageRanges( value, this.getDocumentInfoInternal().pageCount); - return result instanceof Array; - }, + return Array.isArray(result); + } /** * @return {!print_preview.PageNumberSet} Set of page numbers defined by the * page range string. */ - getPageNumberSet: function() { + getPageNumberSet() { var pageNumberList = pageRangeTextToPageList( this.getValueAsString_(), this.getDocumentInfoInternal().pageCount); return new print_preview.PageNumberSet(pageNumberList); - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return true; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { return ''; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return ''; - }, + } /** * @return {string} The value of the ticket item as a string. * @private */ - getValueAsString_: function() { + getValueAsString_() { return /** @type {string} */ (this.getValue()); - }, + } /** * @return {!Array<Object<{from: number, to: number}>>} A list of page * ranges. */ - getPageRanges: function() { + getPageRanges() { var pageRanges = pageRangeTextToPageRanges(this.getValueAsString_()); - return pageRanges instanceof Array ? pageRanges : []; - }, + return Array.isArray(pageRanges) ? pageRanges : []; + } /** * @return {!Array<Object<{from: number, to: number}>>} A list of page @@ -85,29 +73,37 @@ cr.define('print_preview.ticket_items', function() { * TODO(vitalybuka): this should be removed when native layer switched to * page ranges. */ - getDocumentPageRanges: function() { + getDocumentPageRanges() { var pageRanges = pageRangeTextToPageRanges( this.getValueAsString_(), this.getDocumentInfoInternal().pageCount); - return pageRanges instanceof Array ? pageRanges : []; - }, + return Array.isArray(pageRanges) ? pageRanges : []; + } /** * @return {!number} Number of pages reported by the document. */ - getDocumentNumPages: function() { + getDocumentNumPages() { return this.getDocumentInfoInternal().pageCount; - }, + } /** * @return {!PageRangeStatus} */ - checkValidity: function() { + checkValidity() { var pageRanges = pageRangeTextToPageRanges( this.getValueAsString_(), this.getDocumentInfoInternal().pageCount); - return pageRanges instanceof Array ? PageRangeStatus.NO_ERROR : - pageRanges; - }, - }; + return Array.isArray(pageRanges) ? PageRangeStatus.NO_ERROR : pageRanges; + } + } + + /** + * Impossibly large page number. + * @type {number} + * @const + * @private + */ + PageRange.MAX_PAGE_NUMBER_ = 1000000000; + // Export return {PageRange: PageRange}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/rasterize.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/rasterize.js index 5827ba55025..15771711264 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/rasterize.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/rasterize.js @@ -5,43 +5,39 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Rasterize ticket item whose value is a {@code boolean} that indicates - * whether the PDF document should be rendered as images. - * @constructor - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print, used to determine if document is a PDF. - * @extends {print_preview.ticket_items.TicketItem} - */ - function Rasterize(destinationStore, documentInfo) { - print_preview.ticket_items.TicketItem.call( - this, null /* appState */, null /* field */, - null /* destinationStore */, documentInfo); - } - - Rasterize.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class Rasterize extends print_preview.ticket_items.TicketItem { + /** + * Rasterize ticket item whose value is a {@code boolean} that indicates + * whether the PDF document should be rendered as images. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print, used to determine if document is a PDF. + */ + constructor(destinationStore, documentInfo) { + super( + null /* appState */, null /* field */, null /* destinationStore */, + documentInfo); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return !this.getDocumentInfoInternal().isModifiable; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { return false; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return this.getDefaultValueInternal(); } - }; + } // Export return {Rasterize: Rasterize}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/scaling.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/scaling.js index e51252fce26..6ecc989d0e1 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/scaling.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/scaling.js @@ -5,39 +5,36 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Scaling ticket item whose value is a {@code string} that indicates what the - * scaling (in percent) of the document should be. The ticket item is backed - * by a string since the user can textually input the scaling value. - * @param {!print_preview.AppState} appState App state to persist item value. - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print. - * @param {!print_preview.DestinationStore} destinationStore Used to determine - * whether fit to page should be available. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function Scaling(appState, destinationStore, documentInfo) { - print_preview.ticket_items.TicketItem.call( - this, appState, print_preview.AppStateField.SCALING, destinationStore, - documentInfo); - } - - Scaling.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class Scaling extends print_preview.ticket_items.TicketItem { + /** + * Scaling ticket item whose value is a {@code string} that indicates what + * the scaling (in percent) of the document should be. The ticket item is + * backed by a string since the user can textually input the scaling value. + * @param {!print_preview.AppState} appState App state to persist item + * value. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @param {!print_preview.DestinationStore} destinationStore Used to + * determine whether scaling should be available. + */ + constructor(appState, destinationStore, documentInfo) { + super( + appState, print_preview.AppStateField.SCALING, destinationStore, + documentInfo); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return value != ''; - }, + } /** @override */ - isValueEqual: function(value) { + isValueEqual(value) { return this.getValue() == value; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { // This is not a function of the printer, but should be disabled if we are // saving a PDF to a PDF. var knownSizeToSaveAsPdf = @@ -47,25 +44,25 @@ cr.define('print_preview.ticket_items', function() { this.getSelectedDestInternal().id == print_preview.Destination.GooglePromotedId.SAVE_AS_PDF; return !knownSizeToSaveAsPdf; - }, + } /** @return {number} The scaling percentage indicated by the ticket item. */ - getValueAsNumber: function() { + getValueAsNumber() { var value = this.getValue() == '' ? 0 : parseInt(this.getValue(), 10); assert(!isNaN(value)); return value; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { return '100'; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return '100'; - }, - }; + } + } // Export return {Scaling: Scaling}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/selection_only.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/selection_only.js index f6bfaf73c62..75c5554be5e 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/selection_only.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/selection_only.js @@ -5,44 +5,40 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * Ticket item whose value is a {@code boolean} that represents whether to - * print selection only. - * @param {!print_preview.DocumentInfo} documentInfo Information about the - * document to print. - * @constructor - * @extends {print_preview.ticket_items.TicketItem} - */ - function SelectionOnly(documentInfo) { - print_preview.ticket_items.TicketItem.call( - this, null /*appState*/, null /*field*/, null /*destinationStore*/, - documentInfo); - } - - SelectionOnly.prototype = { - __proto__: print_preview.ticket_items.TicketItem.prototype, + class SelectionOnly extends print_preview.ticket_items.TicketItem { + /** + * Ticket item whose value is a {@code boolean} that represents whether to + * print selection only. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + */ + constructor(documentInfo) { + super( + null /*appState*/, null /*field*/, null /*destinationStore*/, + documentInfo); + } /** @override */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { return true; - }, + } /** @override */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return this.getDocumentInfoInternal().isModifiable && this.getDocumentInfoInternal().hasSelection; - }, + } /** @override */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { return false; - }, + } /** @override */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { return false; } - }; + } // Export return {SelectionOnly: SelectionOnly}; diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js index ce3da11b16e..9d543289ea7 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js @@ -9,103 +9,92 @@ print_preview.ValueType; cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * An object that represents a user modifiable item in a print ticket. Each - * ticket item has a value which can be set by the user. Ticket items can also - * be unavailable for modifying if the print destination doesn't support it or - * if other ticket item constraints are not met. - * @param {?print_preview.AppState} appState Application state model to update - * when ticket items update. - * @param {?print_preview.AppStateField} field Field of the app state to - * update when ticket item is updated. - * @param {?print_preview.DestinationStore} destinationStore Used listen for - * changes in the currently selected destination's capabilities. Since - * this is a common dependency of ticket items, it's handled in the base - * class. - * @param {?print_preview.DocumentInfo=} opt_documentInfo Used to listen for - * changes in the document. Since this is a common dependency of ticket - * items, it's handled in the base class. - * @constructor - * @extends {cr.EventTarget} - */ - function TicketItem(appState, field, destinationStore, opt_documentInfo) { - cr.EventTarget.call(this); - - /** - * Application state model to update when ticket items update. - * @type {print_preview.AppState} - * @private - */ - this.appState_ = appState || null; - - /** - * Field of the app state to update when ticket item is updated. - * @type {?print_preview.AppStateField} - * @private - */ - this.field_ = field || null; - - /** - * Used listen for changes in the currently selected destination's - * capabilities. - * @type {print_preview.DestinationStore} - * @private - */ - this.destinationStore_ = destinationStore || null; - - /** - * Used to listen for changes in the document. - * @type {print_preview.DocumentInfo} - * @private - */ - this.documentInfo_ = opt_documentInfo || null; - - /** - * Backing store of the print ticket item. - * @type {Object} - * @private - */ - this.value_ = null; - - /** - * Keeps track of event listeners for the ticket item. - * @type {!EventTracker} - * @private - */ - this.tracker_ = new EventTracker(); - - this.addEventHandlers_(); - } - - /** - * Event types dispatched by this class. - * @enum {string} - */ - TicketItem.EventType = { - CHANGE: 'print_preview.ticket_items.TicketItem.CHANGE' - }; - - TicketItem.prototype = { - __proto__: cr.EventTarget.prototype, + class TicketItem extends cr.EventTarget { + /** + * An object that represents a user modifiable item in a print ticket. Each + * ticket item has a value which can be set by the user. Ticket items can + * also be unavailable for modifying if the print destination doesn't + * support it or if other ticket item constraints are not met. + * @param {?print_preview.AppState} appState Application state model to + * update + * when ticket items update. + * @param {?print_preview.AppStateField} field Field of the app state to + * update when ticket item is updated. + * @param {?print_preview.DestinationStore} destinationStore Used listen for + * changes in the currently selected destination's capabilities. Since + * this is a common dependency of ticket items, it's handled in the base + * class. + * @param {?print_preview.DocumentInfo=} opt_documentInfo Used to listen for + * changes in the document. Since this is a common dependency of ticket + * items, it's handled in the base class. + */ + constructor(appState, field, destinationStore, opt_documentInfo) { + super(); + + /** + * Application state model to update when ticket items update. + * @type {print_preview.AppState} + * @private + */ + this.appState_ = appState || null; + + /** + * Field of the app state to update when ticket item is updated. + * @type {?print_preview.AppStateField} + * @private + */ + this.field_ = field || null; + + /** + * Used listen for changes in the currently selected destination's + * capabilities. + * @type {print_preview.DestinationStore} + * @private + */ + this.destinationStore_ = destinationStore || null; + + /** + * Used to listen for changes in the document. + * @type {print_preview.DocumentInfo} + * @private + */ + this.documentInfo_ = opt_documentInfo || null; + + /** + * Backing store of the print ticket item. + * @type {Object} + * @private + */ + this.value_ = null; + + /** + * Keeps track of event listeners for the ticket item. + * @type {!EventTracker} + * @private + */ + this.tracker_ = new EventTracker(); + + this.addEventHandlers_(); + } /** * Determines whether a given value is valid for the ticket item. * @param {?} value The value to check for validity. * @return {boolean} Whether the given value is valid for the ticket item. */ - wouldValueBeValid: function(value) { + wouldValueBeValid(value) { throw Error('Abstract method not overridden'); - }, + } /** * @return {boolean} Whether the print destination capability is available. */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { throw Error('Abstract method not overridden'); - }, + } /** @return {print_preview.ValueType} The value of the ticket item. */ - getValue: function() { + getValue() { if (!this.isCapabilityAvailable()) { return this.getCapabilityNotAvailableValueInternal(); } @@ -113,34 +102,34 @@ cr.define('print_preview.ticket_items', function() { return this.getDefaultValueInternal(); } return this.value_; - }, + } /** @return {boolean} Whether the ticket item was modified by the user. */ - isUserEdited: function() { + isUserEdited() { return this.value_ != null; - }, + } /** @return {boolean} Whether the ticket item's value is valid. */ - isValid: function() { + isValid() { if (!this.isUserEdited()) { return true; } return this.wouldValueBeValid(this.value_); - }, + } /** * @param {?} value Value to compare to the value of this ticket item. * @return {boolean} Whether the given value is equal to the value of the * ticket item. */ - isValueEqual: function(value) { + isValueEqual(value) { return this.getValue() == value; - }, + } /** * @param {?} value Value to set as the value of the ticket item. */ - updateValue: function(value) { + updateValue(value) { // Use comparison with capabilities for event. var sendUpdateEvent = !this.isValueEqual(value); // Don't lose requested value if capability is not available. @@ -152,77 +141,77 @@ cr.define('print_preview.ticket_items', function() { } if (sendUpdateEvent) cr.dispatchSimpleEvent(this, TicketItem.EventType.CHANGE); - }, + } /** * @return {?} Default value of the ticket item if no value was set by * the user. * @protected */ - getDefaultValueInternal: function() { + getDefaultValueInternal() { throw Error('Abstract method not overridden'); - }, + } /** * @return {?} Default value of the ticket item if the capability is * not available. * @protected */ - getCapabilityNotAvailableValueInternal: function() { + getCapabilityNotAvailableValueInternal() { throw Error('Abstract method not overridden'); - }, + } /** * @return {!EventTracker} Event tracker to keep track of events from * dependencies. * @protected */ - getTrackerInternal: function() { + getTrackerInternal() { return this.tracker_; - }, + } /** * @return {print_preview.Destination} Selected destination from the * destination store, or {@code null} if no destination is selected. * @protected */ - getSelectedDestInternal: function() { + getSelectedDestInternal() { return this.destinationStore_ ? this.destinationStore_.selectedDestination : null; - }, + } /** * @return {print_preview.DocumentInfo} Document data model. * @protected */ - getDocumentInfoInternal: function() { + getDocumentInfoInternal() { return this.documentInfo_; - }, + } /** * Dispatches a CHANGE event. * @protected */ - dispatchChangeEventInternal: function() { + dispatchChangeEventInternal() { cr.dispatchSimpleEvent( this, print_preview.ticket_items.TicketItem.EventType.CHANGE); - }, + } /** * Updates the value of the ticket item without dispatching any events or * persisting the value. * @protected */ - updateValueInternal: function(value) { + updateValueInternal(value) { this.value_ = value; - }, + } /** * Adds event handlers for this class. * @private */ - addEventHandlers_: function() { + addEventHandlers_() { if (this.destinationStore_) { this.tracker_.add( this.destinationStore_, @@ -235,9 +224,18 @@ cr.define('print_preview.ticket_items', function() { this.documentInfo_, print_preview.DocumentInfo.EventType.CHANGE, this.dispatchChangeEventInternal.bind(this)); } - }, + } + } + + /** + * Event types dispatched by this class. + * @enum {string} + */ + TicketItem.EventType = { + CHANGE: 'print_preview.ticket_items.TicketItem.CHANGE' }; + // Export return {TicketItem: TicketItem}; }); diff --git a/chromium/chrome/browser/resources/print_preview/data/ticket_items/vendor_items.js b/chromium/chrome/browser/resources/print_preview/data/ticket_items/vendor_items.js index 82a65d361f5..0bdcd9f9e5c 100644 --- a/chromium/chrome/browser/resources/print_preview/data/ticket_items/vendor_items.js +++ b/chromium/chrome/browser/resources/print_preview/data/ticket_items/vendor_items.js @@ -5,60 +5,56 @@ cr.define('print_preview.ticket_items', function() { 'use strict'; - /** - * An object that represents a user modifiable item in a print ticket. Each - * ticket item has a value which can be set by the user. Ticket items can also - * be unavailable for modifying if the print destination doesn't support it or - * if other ticket item constraints are not met. - * @param {print_preview.AppState} appState Application state model to update - * when ticket items update. - * @param {print_preview.DestinationStore} destinationStore Used listen for - * changes in the currently selected destination's capabilities. Since - * this is a common dependency of ticket items, it's handled in the base - * class. - * @constructor - * @extends {cr.EventTarget} - */ - function VendorItems(appState, destinationStore) { - cr.EventTarget.call(this); - + class VendorItems extends cr.EventTarget { /** - * Application state model to update when ticket items update. - * @private {print_preview.AppState} + * An object that represents a user modifiable item in a print ticket. Each + * ticket item has a value which can be set by the user. Ticket items can + * also be unavailable for modifying if the print destination doesn't + * support it or if other ticket item constraints are not met. + * @param {print_preview.AppState} appState Application state model to + * update when ticket items update. + * @param {print_preview.DestinationStore} destinationStore Used listen for + * changes in the currently selected destination's capabilities. Since + * this is a common dependency of ticket items, it's handled in the + * base class. */ - this.appState_ = appState || null; + constructor(appState, destinationStore) { + super(); - /** - * Used listen for changes in the currently selected destination's - * capabilities. - * @private {print_preview.DestinationStore} - */ - this.destinationStore_ = destinationStore || null; + /** + * Application state model to update when ticket items update. + * @private {print_preview.AppState} + */ + this.appState_ = appState || null; - /** - * Vendor ticket items store, maps item id to the item value. - * @private {!Object<string>} - */ - this.items_ = {}; - } + /** + * Used listen for changes in the currently selected destination's + * capabilities. + * @private {print_preview.DestinationStore} + */ + this.destinationStore_ = destinationStore || null; - VendorItems.prototype = { - __proto__: cr.EventTarget.prototype, + /** + * Vendor ticket items store, maps item id to the item value. + * @private {!Object<string>} + */ + this.items_ = {}; + } /** @return {boolean} Whether vendor capabilities are available. */ - isCapabilityAvailable: function() { + isCapabilityAvailable() { return !!this.capability; - }, + } /** @return {boolean} Whether the ticket item was modified by the user. */ - isUserEdited: function() { + isUserEdited() { // If there's at least one ticket item stored in values, it was edited. for (var key in this.items_) { if (this.items_.hasOwnProperty(key)) return true; } return false; - }, + } /** @return {Object} Vendor capabilities of the selected destination. */ get capability() { @@ -73,7 +69,7 @@ cr.define('print_preview.ticket_items', function() { return (destination.capabilities && destination.capabilities.printer && destination.capabilities.printer.vendor_capability) || null; - }, + } /** * Vendor ticket items store, maps item id to the item value. @@ -81,13 +77,13 @@ cr.define('print_preview.ticket_items', function() { */ get ticketItems() { return this.items_; - }, + } /** * @param {!Object<string>} values Values to set as the values of vendor * ticket items. Maps vendor item id to the value. */ - updateValue: function(values) { + updateValue(values) { this.items_ = {}; if (typeof values == 'object') { for (var key in values) { @@ -103,7 +99,7 @@ cr.define('print_preview.ticket_items', function() { print_preview.AppStateField.VENDOR_OPTIONS, this.items_); } } - }; + } // Export return {VendorItems: VendorItems}; diff --git a/chromium/chrome/browser/resources/print_preview/images/printer.png b/chromium/chrome/browser/resources/print_preview/images/1x/printer.png Binary files differindex a0a85bbc236..a0a85bbc236 100644 --- a/chromium/chrome/browser/resources/print_preview/images/printer.png +++ b/chromium/chrome/browser/resources/print_preview/images/1x/printer.png diff --git a/chromium/chrome/browser/resources/print_preview/images/printer_shared.png b/chromium/chrome/browser/resources/print_preview/images/1x/printer_shared.png Binary files differindex 026ed450f86..026ed450f86 100644 --- a/chromium/chrome/browser/resources/print_preview/images/printer_shared.png +++ b/chromium/chrome/browser/resources/print_preview/images/1x/printer_shared.png diff --git a/chromium/chrome/browser/resources/print_preview/images/2x/printer.png b/chromium/chrome/browser/resources/print_preview/images/2x/printer.png Binary files differnew file mode 100644 index 00000000000..b704e02f841 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/images/2x/printer.png diff --git a/chromium/chrome/browser/resources/print_preview/images/2x/printer_shared.png b/chromium/chrome/browser/resources/print_preview/images/2x/printer_shared.png Binary files differnew file mode 100644 index 00000000000..bbddfd04d2c --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/images/2x/printer_shared.png diff --git a/chromium/chrome/browser/resources/print_preview/native_layer.js b/chromium/chrome/browser/resources/print_preview/native_layer.js index 05072dc29e0..3aaa9947da1 100644 --- a/chromium/chrome/browser/resources/print_preview/native_layer.js +++ b/chromium/chrome/browser/resources/print_preview/native_layer.js @@ -28,14 +28,22 @@ print_preview.LocalDestinationInfo; /** * @typedef {{ - * printerId: string, + * isInKioskAutoPrintMode: boolean, + * isInAppKioskMode: boolean, + * thousandsDelimeter: string, + * decimalDelimeter: string, + * unitType: !print_preview.MeasurementSystemUnitType, + * previewModifiable: boolean, + * documentTitle: string, + * documentHasSelection: boolean, + * shouldPrintSelectionOnly: boolean, * printerName: string, - * printerDescription: string, - * cupsEnterprisePrinter: (boolean | undefined), - * capabilities: !print_preview.Cdd, + * serializedAppStateStr: ?string, + * serializedDefaultDestinationSelectionRulesStr: ?string, * }} + * @see corresponding field name definitions in print_preview_handler.cc */ -print_preview.PrinterCapabilitiesResponse; +print_preview.NativeInitialSettings; /** * @typedef {{ @@ -51,11 +59,13 @@ print_preview.PrivetPrinterDescription; /** * @typedef {{ - * printer: !print_preview.PrivetPrinterDescription, + * printer:(print_preview.PrivetPrinterDescription | + * print_preview.LocalDestinationInfo | + * undefined), * capabilities: !print_preview.Cdd, * }} */ -print_preview.PrivetPrinterCapabilitiesResponse; +print_preview.CapabilitiesResponse; /** * @typedef {{ @@ -85,6 +95,8 @@ print_preview.ProvisionalDestinationInfo; print_preview.PrinterType = { PRIVET_PRINTER: 0, EXTENSION_PRINTER: 1, + PDF_PRINTER: 2, + LOCAL_PRINTER: 3, }; cr.define('print_preview', function() { @@ -126,104 +138,35 @@ cr.define('print_preview', function() { * @return {!Promise<!print_preview.NativeInitialSettings>} */ getInitialSettings() { - return cr.sendWithPromise('getInitialSettings') - .then( - /** - * @param {!Object} initialSettings Object containing the raw - * Print Preview settings. - */ - function(initialSettings) { - var numberFormatSymbols = - print_preview.MeasurementSystem.parseNumberFormat( - initialSettings['numberFormat']); - var unitType = print_preview.MeasurementSystemUnitType.IMPERIAL; - if (initialSettings['measurementSystem'] != null) { - unitType = initialSettings['measurementSystem']; - } - return new print_preview.NativeInitialSettings( - initialSettings['printAutomaticallyInKioskMode'] || false, - initialSettings['appKioskMode'] || false, - numberFormatSymbols[0] || ',', - numberFormatSymbols[1] || '.', unitType, - initialSettings['previewModifiable'] || false, - initialSettings['initiatorTitle'] || '', - initialSettings['documentHasSelection'] || false, - initialSettings['shouldPrintSelectionOnly'] || false, - initialSettings['printerName'] || null, - initialSettings['appState'] || null, - initialSettings['defaultDestinationSelectionRules'] || - null); - }); - } - - /** - * Requests the system's local print destinations. The promise will be - * resolved with a list of the local destinations. - * @return {!Promise<!Array<print_preview.LocalDestinationInfo>>} - */ - getPrinters() { - return cr.sendWithPromise('getPrinters'); + return cr.sendWithPromise('getInitialSettings'); } /** - * Requests the network's privet print destinations. After this is called, - * a number of privet-printer-changed events may be fired. - * @return {!Promise} Resolves when privet printer search is completed. - * Rejected if privet printers are not enabled. + * Requests the system's print destinations. The promise will be resolved + * when all destinations of that type have been retrieved. One or more + * 'printers-added' events may be fired in response before resolution. + * @param {!print_preview.PrinterType} type The type of destinations to + * request. + * @return {!Promise} */ - getPrivetPrinters() { - return cr.sendWithPromise( - 'getExtensionOrPrivetPrinters', - print_preview.PrinterType.PRIVET_PRINTER); - } - - /** - * Request a list of extension printers. Printers are reported as they are - * found by a series of 'extension-printers-added' events. - * @return {!Promise} Will be resolved when all extension managed printers - * have been sent. - */ - getExtensionPrinters() { - return cr.sendWithPromise( - 'getExtensionOrPrivetPrinters', - print_preview.PrinterType.EXTENSION_PRINTER); + getPrinters(type) { + return cr.sendWithPromise('getPrinters', type); } /** * Requests the destination's printing capabilities. Returns a promise that * will be resolved with the capabilities if they are obtained successfully. * @param {string} destinationId ID of the destination. - * @return {!Promise<!print_preview.PrinterCapabilitiesResponse>} - */ - getPrinterCapabilities(destinationId) { - return cr.sendWithPromise('getPrinterCapabilities', destinationId); - } - - /** - * Requests the privet destination's printing capabilities. Returns a - * promise that will be resolved with capabilities and printer information - * if capabilities are obtained successfully. - * @param {string} destinationId The ID of the destination - * @return {!Promise<!print_preview.PrivetPrinterCapabilitiesResponse>} - */ - getPrivetPrinterCapabilities(destinationId) { - return cr.sendWithPromise( - 'getExtensionOrPrivetPrinterCapabilities', destinationId, - print_preview.PrinterType.PRIVET_PRINTER); - } - - /** - * Requests the extension destination's printing capabilities. Returns a - * promise that will be resolved with the capabilities if capabilities are - * obtained successfully. - * @param {string} destinationId The ID of the destination whose - * capabilities are requested. - * @return {!Promise<!print_preview.Cdd>} + * @param {!print_preview.PrinterType} type The destination's printer type. + * @return {!Promise<!print_preview.CapabilitiesResponse>} */ - getExtensionPrinterCapabilities(destinationId) { + getPrinterCapabilities(destinationId, type) { return cr.sendWithPromise( - 'getExtensionOrPrivetPrinterCapabilities', destinationId, - print_preview.PrinterType.EXTENSION_PRINTER); + 'getPrinterCapabilities', destinationId, + destinationId == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF ? + print_preview.PrinterType.PDF_PRINTER : + type); } /** @@ -571,185 +514,8 @@ cr.define('print_preview', function() { */ NativeLayer.SERIALIZED_STATE_VERSION_ = 1; - /** - * Initial settings retrieved from the native layer. - */ - class NativeInitialSettings { - /** - * @param {boolean} isInKioskAutoPrintMode Whether the print preview should - * be in auto-print mode. - * @param {boolean} isInAppKioskMode Whether the print preview is in App - * Kiosk mode. - * @param {string} thousandsDelimeter Character delimeter of thousands - * digits. - * @param {string} decimalDelimeter Character delimeter of the decimal - * point. - * @param {!print_preview.MeasurementSystemUnitType} unitType Unit type of - * local machine's measurement system. - * @param {boolean} isDocumentModifiable Whether the document to print is - * modifiable. - * @param {string} documentTitle Title of the document. - * @param {boolean} documentHasSelection Whether the document has selected - * content. - * @param {boolean} selectionOnly Whether only selected content should be - * printed. - * @param {?string} systemDefaultDestinationId ID of the system default - * destination. - * @param {?string} serializedAppStateStr Serialized app state. - * @param {?string} serializedDefaultDestinationSelectionRulesStr Serialized - * default destination selection rules. - */ - constructor( - isInKioskAutoPrintMode, isInAppKioskMode, thousandsDelimeter, - decimalDelimeter, unitType, isDocumentModifiable, documentTitle, - documentHasSelection, selectionOnly, systemDefaultDestinationId, - serializedAppStateStr, serializedDefaultDestinationSelectionRulesStr) { - /** - * Whether the print preview should be in auto-print mode. - * @private {boolean} - */ - this.isInKioskAutoPrintMode_ = isInKioskAutoPrintMode; - - /** - * Whether the print preview should switch to App Kiosk mode. - * @private {boolean} - */ - this.isInAppKioskMode_ = isInAppKioskMode; - - /** - * Character delimeter of thousands digits. - * @private {string} - */ - this.thousandsDelimeter_ = thousandsDelimeter; - - /** - * Character delimeter of the decimal point. - * @private {string} - */ - this.decimalDelimeter_ = decimalDelimeter; - - /** - * Unit type of local machine's measurement system. - * @private {print_preview.MeasurementSystemUnitType} - */ - this.unitType_ = unitType; - - /** - * Whether the document to print is modifiable. - * @private {boolean} - */ - this.isDocumentModifiable_ = isDocumentModifiable; - - /** - * Title of the document. - * @private {string} - */ - this.documentTitle_ = documentTitle; - - /** - * Whether the document has selection. - * @private {boolean} - */ - this.documentHasSelection_ = documentHasSelection; - - /** - * Whether selection only should be printed. - * @private {boolean} - */ - this.selectionOnly_ = selectionOnly; - - /** - * ID of the system default destination. - * @private {?string} - */ - this.systemDefaultDestinationId_ = systemDefaultDestinationId; - - /** - * Serialized app state. - * @private {?string} - */ - this.serializedAppStateStr_ = serializedAppStateStr; - - /** - * Serialized default destination selection rules. - * @private {?string} - */ - this.serializedDefaultDestinationSelectionRulesStr_ = - serializedDefaultDestinationSelectionRulesStr; - } - - /** - * @return {boolean} Whether the print preview should be in auto-print mode. - */ - get isInKioskAutoPrintMode() { - return this.isInKioskAutoPrintMode_; - } - - /** - * @return {boolean} Whether the print preview should switch to App Kiosk - * mode. - */ - get isInAppKioskMode() { - return this.isInAppKioskMode_; - } - - /** @return {string} Character delimeter of thousands digits. */ - get thousandsDelimeter() { - return this.thousandsDelimeter_; - } - - /** @return {string} Character delimeter of the decimal point. */ - get decimalDelimeter() { - return this.decimalDelimeter_; - } - - /** - * @return {!print_preview.MeasurementSystemUnitType} Unit type of local - * machine's measurement system. - */ - get unitType() { - return this.unitType_; - } - - /** @return {boolean} Whether the document to print is modifiable. */ - get isDocumentModifiable() { - return this.isDocumentModifiable_; - } - - /** @return {string} Document title. */ - get documentTitle() { - return this.documentTitle_; - } - - /** @return {boolean} Whether the document has selection. */ - get documentHasSelection() { - return this.documentHasSelection_; - } - - /** @return {boolean} Whether selection only should be printed. */ - get selectionOnly() { - return this.selectionOnly_; - } - - /** @return {?string} ID of the system default destination. */ - get systemDefaultDestinationId() { - return this.systemDefaultDestinationId_; - } - - /** @return {?string} Serialized app state. */ - get serializedAppStateStr() { - return this.serializedAppStateStr_; - } - - /** @return {?string} Serialized default destination selection rules. */ - get serializedDefaultDestinationSelectionRulesStr() { - return this.serializedDefaultDestinationSelectionRulesStr_; - } - } - // Export return { - NativeInitialSettings: NativeInitialSettings, NativeLayer: NativeLayer }; }); diff --git a/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js b/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js index 3ef1805c88b..98e8f1c9add 100644 --- a/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js +++ b/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js @@ -318,8 +318,8 @@ cr.define('print_preview', function() { this.onOpenSystemDialogButtonClick_.bind(this)); var TicketStoreEvent = print_preview.PrintTicketStore.EventType; - [TicketStoreEvent.INITIALIZE, TicketStoreEvent.TICKET_CHANGE, - TicketStoreEvent.CAPABILITIES_CHANGE, TicketStoreEvent.DOCUMENT_CHANGE] + [TicketStoreEvent.INITIALIZE, TicketStoreEvent.CAPABILITIES_CHANGE, + TicketStoreEvent.DOCUMENT_CHANGE] .forEach(eventType => { this.tracker.add( this.printTicketStore_, eventType, diff --git a/chromium/chrome/browser/resources/print_preview/print_header.js b/chromium/chrome/browser/resources/print_preview/print_header.js index 810e286d16f..b8f12871fe8 100644 --- a/chromium/chrome/browser/resources/print_preview/print_header.js +++ b/chromium/chrome/browser/resources/print_preview/print_header.js @@ -108,35 +108,19 @@ cr.define('print_preview', function() { this.tracker.add( this.printTicketStore_, print_preview.PrintTicketStore.EventType.INITIALIZE, - this.onTicketChange_.bind(this)); + this.onTicketChange.bind(this)); this.tracker.add( this.printTicketStore_, print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, - this.onTicketChange_.bind(this)); - this.tracker.add( - this.printTicketStore_, - print_preview.PrintTicketStore.EventType.TICKET_CHANGE, - this.onTicketChange_.bind(this)); + this.onTicketChange.bind(this)); this.tracker.add( this.destinationStore_, print_preview.DestinationStore.EventType.DESTINATION_SELECT, this.onDestinationSelect_.bind(this)); this.tracker.add( - this.printTicketStore_.copies, - print_preview.ticket_items.TicketItem.EventType.CHANGE, - this.onTicketChange_.bind(this)); - this.tracker.add( this.printTicketStore_.duplex, print_preview.ticket_items.TicketItem.EventType.CHANGE, - this.onTicketChange_.bind(this)); - this.tracker.add( - this.printTicketStore_.pageRange, - print_preview.ticket_items.TicketItem.EventType.CHANGE, - this.onTicketChange_.bind(this)); - this.tracker.add( - this.printTicketStore_.scaling, - print_preview.ticket_items.TicketItem.EventType.CHANGE, - this.updatePrintButtonEnabledState_.bind(this)); + this.onTicketChange.bind(this)); }, /** @@ -266,9 +250,8 @@ cr.define('print_preview', function() { /** * Called when the print ticket has changed. Disables the print button if * any of the settings are invalid. - * @private */ - onTicketChange_: function() { + onTicketChange: function() { this.updatePrintButtonEnabledState_(); this.updateSummary_(); if (document.activeElement == null || diff --git a/chromium/chrome/browser/resources/print_preview/print_preview.css b/chromium/chrome/browser/resources/print_preview/print_preview.css index bb69ab6e9ed..d9b3f8f2bdd 100644 --- a/chromium/chrome/browser/resources/print_preview/print_preview.css +++ b/chromium/chrome/browser/resources/print_preview/print_preview.css @@ -62,17 +62,6 @@ header { width: 99%; } -.right-column .checkbox, -.right-column .radio { - margin: 0; -} - -.right-column .checkbox label, -.right-column .radio label { - padding-bottom: 5px; - padding-top: 10px; -} - .right-column .radio input[type='radio'], .right-column label input[type='checkbox'] { --min-size: 13.19px; @@ -105,7 +94,7 @@ header { } .two-column.visible .left-column.multirow { - padding-top: 16px; + padding-top: 13px; vertical-align: top; } @@ -137,12 +126,6 @@ h1 { margin: 8px 20px; } -#print-preview .navbar-link.disabled { - color: rgba(0, 0, 0, .5); - cursor: default; - pointer-events: none; -} - button.loading { cursor: progress; } diff --git a/chromium/chrome/browser/resources/print_preview/print_preview.html b/chromium/chrome/browser/resources/print_preview/print_preview.html index 3373d046f4c..c59e8e9b0d6 100644 --- a/chromium/chrome/browser/resources/print_preview/print_preview.html +++ b/chromium/chrome/browser/resources/print_preview/print_preview.html @@ -15,14 +15,12 @@ <link rel="stylesheet" href="common/search_bubble.css"> <link rel="stylesheet" href="settings/destination_settings.css"> <link rel="stylesheet" href="settings/color_settings.css"> - <link rel="stylesheet" href="settings/copies_settings.css"> <link rel="stylesheet" href="settings/settings_box.css"> <link rel="stylesheet" href="settings/page_settings.css"> <link rel="stylesheet" href="settings/margin_settings.css"> <link rel="stylesheet" href="settings/media_size_settings.css"> <link rel="stylesheet" href="settings/layout_settings.css"> <link rel="stylesheet" href="settings/dpi_settings.css"> - <link rel="stylesheet" href="settings/scaling_settings.css"> <link rel="stylesheet" href="settings/advanced_options_settings.css"> <link rel="stylesheet" href="settings/advanced_settings/advanced_settings.css"> <link rel="stylesheet" href="settings/advanced_settings/advanced_settings_item.css"> diff --git a/chromium/chrome/browser/resources/print_preview/print_preview.js b/chromium/chrome/browser/resources/print_preview/print_preview.js index 00a9b0e427a..bee9f809d16 100644 --- a/chromium/chrome/browser/resources/print_preview/print_preview.js +++ b/chromium/chrome/browser/resources/print_preview/print_preview.js @@ -390,6 +390,10 @@ cr.define('print_preview', function() { this.printIfReady_.bind(this)); this.tracker.add( this.destinationStore_, + print_preview.DestinationStore.EventType.SELECTED_DESTINATION_INVALID, + this.onSelectedDestinationInvalid_.bind(this)); + this.tracker.add( + this.destinationStore_, print_preview.DestinationStore.EventType.DESTINATION_SELECT, this.onDestinationSelect_.bind(this)); @@ -435,6 +439,15 @@ cr.define('print_preview', function() { this.advancedOptionsSettings_, print_preview.AdvancedOptionsSettings.EventType.BUTTON_ACTIVATED, this.onAdvancedOptionsButtonActivated_.bind(this)); + + /* Ticket items that may be invalid. */ + [this.printTicketStore_.copies, this.printTicketStore_.pageRange, + this.printTicketStore_.scaling, + ].forEach((item) => { + this.tracker.add( + item, print_preview.ticket_items.TicketItem.EventType.CHANGE, + this.onTicketChange_.bind(this)); + }); }, /** @override */ @@ -465,9 +478,9 @@ cr.define('print_preview', function() { */ setIsEnabled_: function(isEnabled) { if ($('system-dialog-link')) - $('system-dialog-link').classList.toggle('disabled', !isEnabled); + $('system-dialog-link').disabled = !isEnabled; if ($('open-pdf-in-preview-link')) - $('open-pdf-in-preview-link').classList.toggle('disabled', !isEnabled); + $('open-pdf-in-preview-link').disabled = !isEnabled; this.printHeader_.isEnabled = isEnabled; this.destinationSettings_.isEnabled = isEnabled; this.pageSettings_.isEnabled = isEnabled; @@ -584,7 +597,7 @@ cr.define('print_preview', function() { print_preview.Destination.GooglePromotedId.SAVE_AS_PDF); // Save as PDF resolves when file selection is completed or cancelled. whenPrintDone.then( - this.onFileSelectionComplete_.bind(this), + this.close_.bind(this, false), this.onFileSelectionCancel_.bind(this)); } @@ -645,13 +658,13 @@ cr.define('print_preview', function() { // The following components must be initialized in this order. this.appState_.init(settings.serializedAppStateStr); this.documentInfo_.init( - settings.isDocumentModifiable, settings.documentTitle, + settings.previewModifiable, settings.documentTitle, settings.documentHasSelection); this.printTicketStore_.init( settings.thousandsDelimeter, settings.decimalDelimeter, - settings.unitType, settings.selectionOnly); + settings.unitType, settings.shouldPrintSelectionOnly); this.destinationStore_.init( - settings.isInAppKioskMode, settings.systemDefaultDestinationId, + settings.isInAppKioskMode, settings.printerName, settings.serializedDefaultDestinationSelectionRulesStr); this.appState_.setInitialized(); @@ -735,20 +748,6 @@ cr.define('print_preview', function() { }, /** - * Called from the native layer when save-to-pdf file selection is complete. - * @private - */ - onFileSelectionComplete_: function() { - assert( - this.uiState_ == PrintPreviewUiState_.FILE_SELECTION, - 'File selection completed when not in file-selection state: ' + - this.uiState_); - this.previewArea_.showCustomMessage( - loadTimeData.getString('printingToPDFInProgress')); - this.uiState_ = PrintPreviewUiState_.PRINTING; - }, - - /** * Called after successfully submitting a job to Google Cloud Print. * @param {!Event} event Contains the ID of the submitted print job. * @private @@ -927,6 +926,17 @@ cr.define('print_preview', function() { }, /** + * Called when the destination store fails to fetch capabilities for the + * selected printer. + * @private + */ + onSelectedDestinationInvalid_: function() { + this.previewArea_.showCustomMessage( + loadTimeData.getString('invalidPrinterSettings')); + this.onSettingsInvalid_(); + }, + + /** * Called when native layer receives invalid settings for a print request. * @private */ @@ -937,6 +947,21 @@ cr.define('print_preview', function() { }, /** + * Called when a ticket item that can be invalid is updated. Updates the + * enabled state of the system dialog link on Windows and the open pdf in + * preview link on Mac. + * @private + */ + onTicketChange_: function() { + this.printHeader_.onTicketChange(); + var disable = !this.printHeader_.isPrintButtonEnabled; + if (cr.isWindows && $('system-dialog-link')) + $('system-dialog-link').disabled = disable; + if ($('open-pdf-in-preview-link')) + $('open-pdf-in-preview-link').disabled = disable; + }, + + /** * Called when the destination settings' change button is activated. * Displays the destination search component. * @private diff --git a/chromium/chrome/browser/resources/print_preview/print_preview_utils.js b/chromium/chrome/browser/resources/print_preview/print_preview_utils.js index 7836ad7956d..bfd199c3801 100644 --- a/chromium/chrome/browser/resources/print_preview/print_preview_utils.js +++ b/chromium/chrome/browser/resources/print_preview/print_preview_utils.js @@ -152,7 +152,7 @@ function pageRangeTextToPageRanges(pageRangeText, opt_totalPageCount) { function pageRangeTextToPageList(pageRangeText, totalPageCount) { var pageRanges = pageRangeTextToPageRanges(pageRangeText, totalPageCount); var pageList = []; - if (pageRanges instanceof Array) { + if (Array.isArray(pageRanges)) { for (var i = 0; i < pageRanges.length; ++i) { for (var j = pageRanges[i].from; j <= Math.min(pageRanges[i].to, totalPageCount); ++j) { diff --git a/chromium/chrome/browser/resources/print_preview/search/destination_list_item.js b/chromium/chrome/browser/resources/print_preview/search/destination_list_item.js index d925eef5bda..b52b4938cbd 100644 --- a/chromium/chrome/browser/resources/print_preview/search/destination_list_item.js +++ b/chromium/chrome/browser/resources/print_preview/search/destination_list_item.js @@ -140,6 +140,7 @@ cr.define('print_preview', function() { updateUi_: function() { var iconImg = this.getChildElement('.destination-list-item-icon'); iconImg.src = this.destination_.iconUrl; + iconImg.srcset = this.destination_.srcSet; var nameEl = this.getChildElement('.destination-list-item-name'); var textContent = this.destination_.displayName; diff --git a/chromium/chrome/browser/resources/print_preview/settings/copies_settings.css b/chromium/chrome/browser/resources/print_preview/settings/copies_settings.css deleted file mode 100644 index e2e1b6f9848..00000000000 --- a/chromium/chrome/browser/resources/print_preview/settings/copies_settings.css +++ /dev/null @@ -1,12 +0,0 @@ -/* Copyright (c) 2012 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. */ - -#copies-settings .collate-container { - -webkit-padding-start: 16px; - display: inline-block; -} - -#copies-settings .collate-container label { - padding: 0; -} diff --git a/chromium/chrome/browser/resources/print_preview/settings/destination_settings.js b/chromium/chrome/browser/resources/print_preview/settings/destination_settings.js index f9941182b45..54d1994e9c8 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/destination_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/destination_settings.js @@ -131,6 +131,7 @@ cr.define('print_preview', function() { var iconEl = this.getElement().getElementsByClassName( print_preview.DestinationSettingsClasses_.ICON)[0]; iconEl.src = destination.iconUrl; + iconEl.srcset = destination.srcSet; var hint = destination.hint; var locationEl = this.getElement().getElementsByClassName( diff --git a/chromium/chrome/browser/resources/print_preview/settings/page_settings.js b/chromium/chrome/browser/resources/print_preview/settings/page_settings.js index fe589e1ad3f..edbd64c3e23 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/page_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/page_settings.js @@ -32,28 +32,35 @@ cr.define('print_preview', function() { /** * Custom page range input. - * @type {HTMLInputElement} + * @type {?HTMLInputElement} * @private */ this.customInput_ = null; /** * Custom page range radio button. - * @type {HTMLInputElement} + * @type {?HTMLInputElement} * @private */ this.customRadio_ = null; /** + * Custom page range label. + * @type {?HTMLElement} + * @private + */ + this.customLabel_ = null; + + /** * All page rage radio button. - * @type {HTMLInputElement} + * @type {?HTMLInputElement} * @private */ this.allRadio_ = null; /** * Container of a hint to show when the custom page range is invalid. - * @type {HTMLElement} + * @type {?HTMLElement} * @private */ this.customHintEl_ = null; @@ -68,6 +75,7 @@ cr.define('print_preview', function() { ALL_RADIO: 'page-settings-all-radio', CUSTOM_HINT: 'page-settings-custom-hint', CUSTOM_INPUT: 'page-settings-custom-input', + CUSTOM_LABEL: 'page-settings-print-pages-div', CUSTOM_RADIO: 'page-settings-custom-radio' }; @@ -106,7 +114,7 @@ cr.define('print_preview', function() { assert(this.allRadio_), 'click', this.onAllRadioClick_.bind(this)); this.tracker.add( assert(this.customRadio_), 'click', - this.onCustomRadioClick_.bind(this)); + this.focusCustomInput_.bind(this)); this.tracker.add(customInput, 'blur', this.onCustomInputBlur_.bind(this)); this.tracker.add( customInput, 'focus', this.onCustomInputFocus_.bind(this)); @@ -115,6 +123,9 @@ cr.define('print_preview', function() { this.tracker.add( customInput, 'input', this.onCustomInputChange_.bind(this)); this.tracker.add( + assert(this.customLabel_), 'focus', + this.focusCustomInput_.bind(this)); + this.tracker.add( this.pageRangeTicketItem_, print_preview.ticket_items.TicketItem.EventType.CHANGE, this.onPageRangeTicketItemChange_.bind(this)); @@ -139,6 +150,8 @@ cr.define('print_preview', function() { PageSettings.Classes_.CUSTOM_RADIO)[0]; this.customHintEl_ = this.getElement().getElementsByClassName( PageSettings.Classes_.CUSTOM_HINT)[0]; + this.customLabel_ = this.getElement().getElementsByClassName( + PageSettings.Classes_.CUSTOM_LABEL)[0]; }, /** @@ -179,10 +192,11 @@ cr.define('print_preview', function() { }, /** - * Called when the custom radio button is clicked. Updates the print ticket. + * Focuses the custom input. Called when the custom radio button or custom + * input label is clicked. * @private */ - onCustomRadioClick_: function() { + focusCustomInput_: function() { this.customInput_.focus(); }, diff --git a/chromium/chrome/browser/resources/print_preview/settings/scaling_settings.css b/chromium/chrome/browser/resources/print_preview/settings/scaling_settings.css deleted file mode 100644 index e39db53edd7..00000000000 --- a/chromium/chrome/browser/resources/print_preview/settings/scaling_settings.css +++ /dev/null @@ -1,7 +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. */ - -#scaling-settings .settings-box { - padding-top: 5px; -} diff --git a/chromium/chrome/browser/resources/print_preview/settings/settings_box.css b/chromium/chrome/browser/resources/print_preview/settings/settings_box.css index cf48b061b00..b95a669f277 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/settings_box.css +++ b/chromium/chrome/browser/resources/print_preview/settings/settings_box.css @@ -6,3 +6,14 @@ background: rgb(255, 240, 240); color: rgb(140, 20, 20); } + +.settings-box input[type='number'] { + -webkit-margin-end: 16px; + margin-bottom: 2.5px; + margin-top: 2.5px; +} + +.settings-box { + align-items: center; + display: inline-flex; +} diff --git a/chromium/chrome/browser/resources/print_preview/settings/settings_section_select.js b/chromium/chrome/browser/resources/print_preview/settings/settings_section_select.js index 62ec25c45a3..ab8b1d59fc0 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/settings_section_select.js +++ b/chromium/chrome/browser/resources/print_preview/settings/settings_section_select.js @@ -8,15 +8,21 @@ cr.define('print_preview', function() { /** * Base class for the printer option element visualizing the generic selection * based option. - * @param {!print_preview.ticket_items.TicketItem} ticketItem Ticket item - * visualized by this component. + * @param {(!print_preview.ticket_items.Dpi | + * !print_preview.ticket_items.MediaSize)} ticketItem + * Ticket item visualized by this component. Must have a defined + * capability() getter. * @constructor * @extends {print_preview.SettingsSection} */ function SettingsSectionSelect(ticketItem) { print_preview.SettingsSection.call(this); - /** @private {!print_preview.ticket_items.TicketItem} */ + /** + * {(!print_preview.ticket_items.Dpi | + * !print_preview.ticket_items.MediaSize)} + * @private + */ this.ticketItem_ = ticketItem; } diff --git a/chromium/chrome/browser/resources/profiler/OWNERS b/chromium/chrome/browser/resources/profiler/OWNERS deleted file mode 100644 index 601500194e3..00000000000 --- a/chromium/chrome/browser/resources/profiler/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -eroman@chromium.org -jar@chromium.org - -# COMPONENT: Internals>Metrics diff --git a/chromium/chrome/browser/resources/profiler/profiler.html b/chromium/chrome/browser/resources/profiler/profiler.html deleted file mode 100644 index cc5638f36cc..00000000000 --- a/chromium/chrome/browser/resources/profiler/profiler.html +++ /dev/null @@ -1,164 +0,0 @@ -<!doctype html> -<html> -<head> -<meta charset="utf-8"> -<link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> -<script src="chrome://resources/js/load_time_data.js"></script> -<script src="chrome://resources/js/util.js"></script> -<script src="chrome://profiler/strings.js"></script> -<script src="chrome://profiler/profiler.js"></script> - -<style> - -body { - font-size: 80%; -} - -/* - * The following styles are for a TABLE that uses a thin collapsed border, and - * has a blue heading. When you hover over rows, they turn yellow. - */ -table.results-table { - border-collapse: collapse; -} - -table.results-table, -.results-table th, -.results-table td { - border: 1px solid #777; - padding-left: 4px; - padding-right: 4px; -} - -.results-table th { - background: rgb(224,236,255); -} - -.results-table tbody tr:hover { - background-color: rgb(255, 255, 187); -} - -/* - * Make the column headers change the mouse to a "hand" cursor, sine they are - * clickable. - */ -.results-table th { - cursor: pointer; -} - -/* - * This is row which displays aggregate totals for each column. - */ -.results-table .aggregator-row { - background: rgb(255, 204, 153); -} - -/* - * This is the row at the end of tables which explains how many rows were shown, - * and how many were hidden. - */ -.results-table .truncation-row { - background: #eee; -} - -/*---------------------------------------------------------------------------*/ - -/* - * When grouping data, the table for each bucket is wrapped in a DIV with this - * class. Used to add a bit of spacing between groups. - */ -.group-container { - margin-bottom: 2ex; - margin-top: 2ex; -} - -/* - * The title for each group is enclosed in a DIV of the following class. - */ -.group-title-container { - font-size: 140%; - margin-bottom: 1ex; -} - -/* Styling to make a span look like a clickable link */ -.pseudo-link { - color: blue; - cursor: pointer; - text-decoration: underline; -} - -.selected-snapshot { - color: purple; - font-weight: bold; -} - -#snapshot-selection-summary { - color: green; - font-style: italic; - font-weight: bold; - margin-top: 1ex; -} - -.errormsg { - color: red; -} - -</style> -</head> -<body> - <table width=100%> - <tr> - <td> - <b>Save:</b><button id=save-snapshots-button>Save</button> - <b>Restore:</b> <input type=file id=snapshot-file-loader> - <span id=file-load-error hidden class=errormsg></span> - </td> - <td align=right> - <a target="_blank" - href="https://sites.google.com/a/chromium.org/dev/developers/threaded-task-tracking"> - Profiler Documentation - </a> - </td> - </tr> - </table> - <hr> - <table width=100%> - <tr> - <td> - <b>Group by: </b> <span id=group-by-container></span> - <b>Sort by: </b> <span id=sort-by-container></span> - </td> - <td align=right> - <span id=snapshots-link class=pseudo-link>[snapshots]</span> - <span id=edit-columns-link class=pseudo-link>[columns]</span> - <input type='search' incremental id='filter-search'> - </td> - </tr> - <tr id=edit-columns-row style='display:none'> - <td colspan=2> - <div> - <b>Merge: </b><span id=column-merge-toggles-container></span> - <label><input type=checkbox id='merge-similar-threads-checkbox' checked> - Merge similar threads.</label> - </div> - <div> - <b>Show: </b><span id=column-toggles-container></span> - </div> - </td> - </tr> - <tr id=snapshots-row style='display:none'> - <td colspan=2> - <button id=take-snapshot-button>Add snapshot</button> - <table><tbody id=snapshots-tbody></tbody></table> - <div id=snapshot-selection-summary></div> - </td> - </tr> - </table> - - <hr> - - <div id='results-div'></div> - - <a style="display: none" id="download-anchor" download="profile.json"></a> -</body> -</html> diff --git a/chromium/chrome/browser/resources/profiler/profiler.js b/chromium/chrome/browser/resources/profiler/profiler.js deleted file mode 100644 index 81b1250b8aa..00000000000 --- a/chromium/chrome/browser/resources/profiler/profiler.js +++ /dev/null @@ -1,2371 +0,0 @@ -// Copyright (c) 2012 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. - -var g_browserBridge; -var g_mainView; - -// TODO(eroman): The handling of "max" across snapshots is not correct. -// For starters the browser needs to be aware to generate new maximums. -// Secondly, we need to take into account the "max" of intermediary snapshots, -// not just the terminal ones. - -/** - * Main entry point called once the page has loaded. - */ -function onLoad() { - g_browserBridge = new BrowserBridge(); - g_mainView = new MainView(); -} - -document.addEventListener('DOMContentLoaded', onLoad); - -/** - * This class provides a "bridge" for communicating between the javascript and - * the browser. Used as a singleton. - */ -var BrowserBridge = (function() { - 'use strict'; - - /** - * @constructor - */ - function BrowserBridge() {} - - BrowserBridge.prototype = { - //-------------------------------------------------------------------------- - // Messages sent to the browser - //-------------------------------------------------------------------------- - - sendGetData: function() { - chrome.send('getData'); - }, - - //-------------------------------------------------------------------------- - // Messages received from the browser. - //-------------------------------------------------------------------------- - - receivedData: function(data) { - // TODO(eroman): The browser should give an indication of which snapshot - // this data belongs to. For now we always assume it is for the latest. - g_mainView.addDataToSnapshot(data); - }, - }; - - return BrowserBridge; -})(); - -/** - * This class handles the presentation of our profiler view. Used as a - * singleton. - */ -var MainView = (function() { - 'use strict'; - - // -------------------------------------------------------------------------- - // Important IDs in the HTML document - // -------------------------------------------------------------------------- - - // The search box to filter results. - var FILTER_SEARCH_ID = 'filter-search'; - - // The container node to put all the "Group by" dropdowns into. - var GROUP_BY_CONTAINER_ID = 'group-by-container'; - - // The container node to put all the "Sort by" dropdowns into. - var SORT_BY_CONTAINER_ID = 'sort-by-container'; - - // The DIV to put all the tables into. - var RESULTS_DIV_ID = 'results-div'; - - // The container node to put all the column (visibility) checkboxes into. - var COLUMN_TOGGLES_CONTAINER_ID = 'column-toggles-container'; - - // The container node to put all the column (merge) checkboxes into. - var COLUMN_MERGE_TOGGLES_CONTAINER_ID = 'column-merge-toggles-container'; - - // The anchor which toggles visibility of column checkboxes. - var EDIT_COLUMNS_LINK_ID = 'edit-columns-link'; - - // The container node to show/hide when toggling the column checkboxes. - var EDIT_COLUMNS_ROW = 'edit-columns-row'; - - // The checkbox which controls whether things like "Worker Threads" and - // "PAC threads" will be merged together. - var MERGE_SIMILAR_THREADS_CHECKBOX_ID = 'merge-similar-threads-checkbox'; - - var TOGGLE_SNAPSHOTS_LINK_ID = 'snapshots-link'; - var SNAPSHOTS_ROW = 'snapshots-row'; - var SNAPSHOT_SELECTION_SUMMARY_ID = 'snapshot-selection-summary'; - var TAKE_SNAPSHOT_BUTTON_ID = 'take-snapshot-button'; - - var SAVE_SNAPSHOTS_BUTTON_ID = 'save-snapshots-button'; - var SNAPSHOT_FILE_LOADER_ID = 'snapshot-file-loader'; - var LOAD_ERROR_ID = 'file-load-error'; - - var DOWNLOAD_ANCHOR_ID = 'download-anchor'; - - // -------------------------------------------------------------------------- - // Row keys - // -------------------------------------------------------------------------- - - // Each row of our data is an array of values rather than a dictionary. This - // avoids some overhead from repeating the key string multiple times, and - // speeds up the property accesses a bit. The following keys are well-known - // indexes into the array for various properties. - // - // Note that the declaration order will also define the default display order. - - var BEGIN_KEY = 1; // Start at 1 rather than 0 to simplify sorting code. - var END_KEY = BEGIN_KEY; - - var KEY_COUNT = END_KEY++; - var KEY_RUN_TIME = END_KEY++; - var KEY_AVG_RUN_TIME = END_KEY++; - var KEY_MAX_RUN_TIME = END_KEY++; - var KEY_QUEUE_TIME = END_KEY++; - var KEY_AVG_QUEUE_TIME = END_KEY++; - var KEY_MAX_QUEUE_TIME = END_KEY++; - if (loadTimeData.getBoolean('enableMemoryTaskProfiler')) { - var KEY_MEMORY_AVG_ALLOC_OPS = END_KEY++; - var KEY_MEMORY_AVG_FREE_OPS = END_KEY++; - var KEY_MEMORY_AVG_NET_BYTES = END_KEY++; - var KEY_MEMORY_MAX_ALLOCATED_BYTES = END_KEY++; - var KEY_MEMORY_ALLOC_OPS = END_KEY++; - var KEY_MEMORY_FREE_OPS = END_KEY++; - var KEY_MEMORY_ALLOCATED_BYTES = END_KEY++; - var KEY_MEMORY_FREED_BYTES = END_KEY++; - var KEY_MEMORY_ALLOC_OVERHEAD_BYTES = END_KEY++; - } - var KEY_BIRTH_THREAD = END_KEY++; - var KEY_DEATH_THREAD = END_KEY++; - var KEY_PROCESS_TYPE = END_KEY++; - var KEY_PROCESS_ID = END_KEY++; - var KEY_FUNCTION_NAME = END_KEY++; - var KEY_SOURCE_LOCATION = END_KEY++; - var KEY_FILE_NAME = END_KEY++; - var KEY_LINE_NUMBER = END_KEY++; - - var NUM_KEYS = END_KEY - BEGIN_KEY; - - // -------------------------------------------------------------------------- - // Aggregators - // -------------------------------------------------------------------------- - - // To generalize computing/displaying the aggregate "counts" for each column, - // we specify an optional "Aggregator" class to use with each property. - - // The following are actually "Aggregator factories". They create an - // aggregator instance by calling 'create()'. The instance is then fed - // each row one at a time via the 'consume()' method. After all rows have - // been consumed, the 'getValueAsText()' method will return the aggregated - // value. - - /** - * This aggregator counts the number of unique values that were fed to it. - */ - var UniquifyAggregator = (function() { - function Aggregator(key) { - this.key_ = key; - this.valuesSet_ = {}; - } - - Aggregator.prototype = { - consume: function(e) { - this.valuesSet_[e[this.key_]] = true; - }, - - getValueAsText: function() { - return getDictionaryKeys(this.valuesSet_).length + ' unique'; - }, - }; - - return { - create: function(key) { - return new Aggregator(key); - } - }; - })(); - - /** - * This aggregator sums a numeric field. - */ - var SumAggregator = (function() { - function Aggregator(key) { - this.key_ = key; - this.sum_ = 0; - } - - Aggregator.prototype = { - consume: function(e) { - this.sum_ += e[this.key_]; - }, - - getValue: function() { - return this.sum_; - }, - - getValueAsText: function() { - return formatNumberAsText(this.getValue()); - }, - }; - - return { - create: function(key) { - return new Aggregator(key); - } - }; - })(); - - /** - * This aggregator computes an average by summing two - * numeric fields, and then dividing the totals. - */ - var AvgAggregator = (function() { - function Aggregator(numeratorKey, divisorKey) { - this.numeratorKey_ = numeratorKey; - this.divisorKey_ = divisorKey; - - this.numeratorSum_ = 0; - this.divisorSum_ = 0; - } - - Aggregator.prototype = { - consume: function(e) { - this.numeratorSum_ += e[this.numeratorKey_]; - this.divisorSum_ += e[this.divisorKey_]; - }, - - getValue: function() { - return this.numeratorSum_ / this.divisorSum_; - }, - - getValueAsText: function() { - return formatNumberAsText(this.getValue()); - }, - }; - - return { - create: function(numeratorKey, divisorKey) { - return { - create: function(key) { - return new Aggregator(numeratorKey, divisorKey); - }, - }; - } - }; - })(); - - /** - * This aggregator computes an average by summing the difference of two - * numeric fields, summing a count, and then dividing the totals. - */ - var AvgDiffAggregator = (function() { - function Aggregator(numeratorPosKey, numeratorNegKey, divisorKey) { - this.numeratorPosKey_ = numeratorPosKey; - this.numeratorNegKey_ = numeratorNegKey; - this.divisorKey_ = divisorKey; - - this.numeratorSum_ = 0; - this.divisorSum_ = 0; - } - - Aggregator.prototype = { - consume: function(e) { - this.numeratorSum_ += - e[this.numeratorPosKey_] - e[this.numeratorNegKey_]; - this.divisorSum_ += e[this.divisorKey_]; - }, - - getValue: function() { - return this.numeratorSum_ / this.divisorSum_; - }, - - getValueAsText: function() { - return formatNumberAsText(this.getValue()); - }, - }; - - return { - create: function(numeratorPosKey, numeratorNegKey, divisorKey) { - return { - create: function(key) { - return new Aggregator(numeratorPosKey, numeratorNegKey, divisorKey); - }, - }; - } - }; - })(); - - /** - * This aggregator finds the maximum for a numeric field. - */ - var MaxAggregator = (function() { - function Aggregator(key) { - this.key_ = key; - this.max_ = -Infinity; - } - - Aggregator.prototype = { - consume: function(e) { - this.max_ = Math.max(this.max_, e[this.key_]); - }, - - getValue: function() { - return this.max_; - }, - - getValueAsText: function() { - return formatNumberAsText(this.getValue()); - }, - }; - - return { - create: function(key) { - return new Aggregator(key); - } - }; - })(); - - // -------------------------------------------------------------------------- - // Key properties - // -------------------------------------------------------------------------- - - // Custom comparator for thread names (sorts main thread and IO thread - // higher than would happen lexicographically.) - var threadNameComparator = createLexicographicComparatorWithExceptions([ - 'CrBrowserMain', - 'Chrome_IOThread', - 'Chrome_FileThread', - 'Chrome_HistoryThread', - 'Chrome_DBThread', - 'Still_Alive', - ]); - - function diffFuncForCount(a, b) { - return b - a; - } - - function diffFuncForMax(a, b) { - return b; - } - - /** - * Enumerates information about various keys. Such as whether their data is - * expected to be numeric or is a string, a descriptive name (title) for the - * property, and what function should be used to aggregate the property when - * displayed in a column. - * - * -------------------------------------- - * The following properties are required: - * -------------------------------------- - * - * [name]: This is displayed as the column's label. - * [aggregator]: Aggregator factory that is used to compute an aggregate - * value for this column. - * - * -------------------------------------- - * The following properties are optional: - * -------------------------------------- - * - * [inputJsonKey]: The corresponding key for this property in the original - * JSON dictionary received from the browser. If this is - * present, values for this key will be automatically - * populated during import. - * [comparator]: A comparator function for sorting this column. - * [textPrinter]: A function that transforms values into the user-displayed - * text shown in the UI. If unspecified, will default to the - * "toString()" function. - * [cellAlignment]: The horizonal alignment to use for columns of this - * property (for instance 'right'). If unspecified will - * default to left alignment. - * [sortDescending]: When first clicking on this column, we will default to - * sorting by |comparator| in ascending order. If this - * property is true, we will reverse that to descending. - * [diff]: Function to call to compute a "difference" value between - * parameters (a, b). This is used when calculating the difference - * between two snapshots. Diffing numeric quantities generally - * involves subtracting, but some fields like max may need to do - * something different. - */ - var KEY_PROPERTIES = []; - - KEY_PROPERTIES[KEY_PROCESS_ID] = { - name: 'PID', - cellAlignment: 'right', - aggregator: UniquifyAggregator, - }; - - KEY_PROPERTIES[KEY_PROCESS_TYPE] = { - name: 'Process type', - aggregator: UniquifyAggregator, - }; - - KEY_PROPERTIES[KEY_BIRTH_THREAD] = { - name: 'Birth thread', - inputJsonKey: 'birth_thread', - aggregator: UniquifyAggregator, - comparator: threadNameComparator, - }; - - KEY_PROPERTIES[KEY_DEATH_THREAD] = { - name: 'Exec thread', - inputJsonKey: 'death_thread', - aggregator: UniquifyAggregator, - comparator: threadNameComparator, - }; - - KEY_PROPERTIES[KEY_FUNCTION_NAME] = { - name: 'Function name', - inputJsonKey: 'birth_location.function_name', - aggregator: UniquifyAggregator, - }; - - KEY_PROPERTIES[KEY_FILE_NAME] = { - name: 'File name', - inputJsonKey: 'birth_location.file_name', - aggregator: UniquifyAggregator, - }; - - KEY_PROPERTIES[KEY_LINE_NUMBER] = { - name: 'Line number', - cellAlignment: 'right', - inputJsonKey: 'birth_location.line_number', - aggregator: UniquifyAggregator, - }; - - KEY_PROPERTIES[KEY_COUNT] = { - name: 'Count', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.count', - aggregator: SumAggregator, - diff: diffFuncForCount, - }; - - KEY_PROPERTIES[KEY_QUEUE_TIME] = { - name: 'Total queue time', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.queue_ms', - aggregator: SumAggregator, - diff: diffFuncForCount, - }; - - KEY_PROPERTIES[KEY_MAX_QUEUE_TIME] = { - name: 'Max queue time', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.queue_ms_max', - aggregator: MaxAggregator, - diff: diffFuncForMax, - }; - - KEY_PROPERTIES[KEY_RUN_TIME] = { - name: 'Total run time', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.run_ms', - aggregator: SumAggregator, - diff: diffFuncForCount, - }; - - if (loadTimeData.getBoolean('enableMemoryTaskProfiler')) { - KEY_PROPERTIES[KEY_MEMORY_AVG_ALLOC_OPS] = { - name: 'Avg Allocations', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - aggregator: AvgAggregator.create(KEY_MEMORY_ALLOC_OPS, KEY_COUNT), - }; - - KEY_PROPERTIES[KEY_MEMORY_AVG_FREE_OPS] = { - name: 'Avg Frees', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - aggregator: AvgAggregator.create(KEY_MEMORY_FREE_OPS, KEY_COUNT), - }; - - KEY_PROPERTIES[KEY_MEMORY_AVG_NET_BYTES] = { - name: 'Avg Net Bytes', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - aggregator: AvgDiffAggregator.create( - KEY_MEMORY_ALLOCATED_BYTES, KEY_MEMORY_FREED_BYTES, KEY_COUNT), - }; - - KEY_PROPERTIES[KEY_MEMORY_ALLOC_OPS] = { - name: 'Allocation count', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.alloc_ops', - aggregator: SumAggregator, - diff: diffFuncForCount, - }; - - KEY_PROPERTIES[KEY_MEMORY_FREE_OPS] = { - name: 'Free Count', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.free_ops', - aggregator: SumAggregator, - diff: diffFuncForCount, - }; - - KEY_PROPERTIES[KEY_MEMORY_ALLOCATED_BYTES] = { - name: 'Allocated bytes', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.allocated_bytes', - aggregator: SumAggregator, - diff: diffFuncForCount, - }; - - KEY_PROPERTIES[KEY_MEMORY_FREED_BYTES] = { - name: 'Freed bytes', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.freed_bytes', - aggregator: SumAggregator, - diff: diffFuncForCount, - }; - - KEY_PROPERTIES[KEY_MEMORY_ALLOC_OVERHEAD_BYTES] = { - name: 'Overhead bytes', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.alloc_overhead_bytes', - aggregator: SumAggregator, - diff: diffFuncForCount, - }; - - KEY_PROPERTIES[KEY_MEMORY_MAX_ALLOCATED_BYTES] = { - name: 'Max allocated (outstanding) bytes', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.max_allocated_bytes', - aggregator: MaxAggregator, - diff: diffFuncForMax, - }; - } - - KEY_PROPERTIES[KEY_AVG_RUN_TIME] = { - name: 'Avg run time', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - aggregator: AvgAggregator.create(KEY_RUN_TIME, KEY_COUNT), - }; - - KEY_PROPERTIES[KEY_MAX_RUN_TIME] = { - name: 'Max run time', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - inputJsonKey: 'death_data.run_ms_max', - aggregator: MaxAggregator, - diff: diffFuncForMax, - }; - - KEY_PROPERTIES[KEY_AVG_QUEUE_TIME] = { - name: 'Avg queue time', - cellAlignment: 'right', - sortDescending: true, - textPrinter: formatNumberAsText, - aggregator: AvgAggregator.create(KEY_QUEUE_TIME, KEY_COUNT), - }; - - KEY_PROPERTIES[KEY_SOURCE_LOCATION] = { - name: 'Source location', - type: 'string', - aggregator: UniquifyAggregator, - }; - - /** - * Returns the string name for |key|. - */ - function getNameForKey(key) { - var props = KEY_PROPERTIES[key]; - if (props == undefined) - throw 'Did not define properties for key: ' + key; - return props.name; - } - - /** - * Ordered list of all keys. This is the order we generally want - * to display the properties in. Default to declaration order. - */ - var ALL_KEYS = []; - for (var k = BEGIN_KEY; k < END_KEY; ++k) - ALL_KEYS.push(k); - - // -------------------------------------------------------------------------- - // Default settings - // -------------------------------------------------------------------------- - - /** - * List of keys for those properties which we want to initially omit - * from the table. (They can be re-enabled by clicking [Edit columns]). - */ - var INITIALLY_HIDDEN_KEYS = [ - KEY_FILE_NAME, - KEY_LINE_NUMBER, - KEY_QUEUE_TIME, - ]; - - if (loadTimeData.getBoolean('enableMemoryTaskProfiler')) { - INITIALLY_HIDDEN_KEYS = INITIALLY_HIDDEN_KEYS.concat([ - KEY_MEMORY_ALLOC_OPS, - KEY_MEMORY_FREE_OPS, - KEY_MEMORY_ALLOCATED_BYTES, - KEY_MEMORY_FREED_BYTES, - KEY_MEMORY_ALLOC_OVERHEAD_BYTES, - ]); - } - - /** - * The ordered list of grouping choices to expose in the "Group by" - * dropdowns. We don't include the numeric properties, since they - * leads to awkward bucketing. - */ - var GROUPING_DROPDOWN_CHOICES = [ - KEY_PROCESS_TYPE, - KEY_PROCESS_ID, - KEY_BIRTH_THREAD, - KEY_DEATH_THREAD, - KEY_FUNCTION_NAME, - KEY_SOURCE_LOCATION, - KEY_FILE_NAME, - KEY_LINE_NUMBER, - ]; - - /** - * The ordered list of sorting choices to expose in the "Sort by" - * dropdowns. - */ - var SORT_DROPDOWN_CHOICES = ALL_KEYS; - - /** - * The ordered list of all columns that can be displayed in the tables (not - * including whatever has been hidden via [Edit Columns]). - */ - var ALL_TABLE_COLUMNS = ALL_KEYS; - - /** - * The initial keys to sort by when loading the page (can be changed later). - */ - var INITIAL_SORT_KEYS = [-KEY_COUNT]; - - /** - * The default sort keys to use when nothing has been specified. - */ - var DEFAULT_SORT_KEYS = [-KEY_COUNT]; - - /** - * The initial keys to group by when loading the page (can be changed later). - */ - var INITIAL_GROUP_KEYS = []; - - /** - * The columns to give the option to merge on. - */ - var MERGEABLE_KEYS = [ - KEY_PROCESS_ID, - KEY_PROCESS_TYPE, - KEY_BIRTH_THREAD, - KEY_DEATH_THREAD, - ]; - - /** - * The columns to merge by default. - */ - var INITIALLY_MERGED_KEYS = []; - - /** - * The full set of columns which define the "identity" for a row. A row is - * considered equivalent to another row if it matches on all of these - * fields. This list is used when merging the data, to determine which rows - * should be merged together. The remaining columns not listed in - * IDENTITY_KEYS will be aggregated. - */ - var IDENTITY_KEYS = [ - KEY_BIRTH_THREAD, - KEY_DEATH_THREAD, - KEY_PROCESS_TYPE, - KEY_PROCESS_ID, - KEY_FUNCTION_NAME, - KEY_SOURCE_LOCATION, - KEY_FILE_NAME, - KEY_LINE_NUMBER, - ]; - - /** - * The time (in milliseconds) to wait after receiving new data before - * re-drawing it to the screen. The reason we wait a bit is to avoid - * repainting repeatedly during the loading phase (which can slow things - * down). Note that this only slows down the addition of new data. It does - * not impact the latency of user-initiated operations like sorting or - * merging. - */ - var PROCESS_DATA_DELAY_MS = 500; - - /** - * The initial number of rows to display (the rest are hidden) when no - * grouping is selected. We use a higher limit than when grouping is used - * since there is a lot of vertical real estate. - */ - var INITIAL_UNGROUPED_ROW_LIMIT = 30; - - /** - * The initial number of rows to display (rest are hidden) for each group. - */ - var INITIAL_GROUP_ROW_LIMIT = 10; - - /** - * The number of extra rows to show/hide when clicking the "Show more" or - * "Show less" buttons. - */ - var LIMIT_INCREMENT = 10; - - // -------------------------------------------------------------------------- - // General utility functions - // -------------------------------------------------------------------------- - - /** - * Returns a list of all the keys in |dict|. - */ - function getDictionaryKeys(dict) { - var keys = []; - for (var key in dict) { - keys.push(key); - } - return keys; - } - - /** - * Formats the number |x| as a decimal integer. Strips off any decimal parts, - * and comma separates the number every 3 characters. - */ - function formatNumberAsText(x) { - var orig = x.toFixed(0); - - var parts = []; - for (var end = orig.length; end > 0;) { - var chunk = Math.min(end, 3); - parts.push(orig.substr(end - chunk, chunk)); - end -= chunk; - } - return parts.reverse().join(','); - } - - /** - * Simple comparator function which works for both strings and numbers. - */ - function simpleCompare(a, b) { - if (a == b) - return 0; - if (a < b) - return -1; - return 1; - } - - /** - * Returns a comparator function that compares values lexicographically, - * but special-cases the values in |orderedList| to have a higher - * rank. - */ - function createLexicographicComparatorWithExceptions(orderedList) { - var valueToRankMap = {}; - for (var i = 0; i < orderedList.length; ++i) - valueToRankMap[orderedList[i]] = i; - - function getCustomRank(x) { - var rank = valueToRankMap[x]; - if (rank == undefined) - rank = Infinity; // Unmatched. - return rank; - } - - return function(a, b) { - var aRank = getCustomRank(a); - var bRank = getCustomRank(b); - - // Not matched by any of our exceptions. - if (aRank == bRank) - return simpleCompare(a, b); - - if (aRank < bRank) - return -1; - return 1; - }; - } - - /** - * Returns dict[key]. Note that if |key| contains periods (.), they will be - * interpreted as meaning a sub-property. - */ - function getPropertyByPath(dict, key) { - var cur = dict; - var parts = key.split('.'); - for (var i = 0; i < parts.length; ++i) { - if (cur == undefined) - return undefined; - cur = cur[parts[i]]; - } - return cur; - } - - /** - * Creates and appends a DOM node of type |tagName| to |parent|. Optionally, - * sets the new node's text to |opt_text|. Returns the newly created node. - */ - function addNode(parent, tagName, opt_text) { - var n = parent.ownerDocument.createElement(tagName); - parent.appendChild(n); - if (opt_text != undefined) { - addText(n, opt_text); - } - return n; - } - - /** - * Adds |text| to |parent|. - */ - function addText(parent, text) { - var textNode = parent.ownerDocument.createTextNode(text); - parent.appendChild(textNode); - return textNode; - } - - /** - * Deletes all the strings in |array| which appear in |valuesToDelete|. - */ - function deleteValuesFromArray(array, valuesToDelete) { - var valueSet = arrayToSet(valuesToDelete); - for (var i = 0; i < array.length;) { - if (valueSet[array[i]]) { - array.splice(i, 1); - } else { - i++; - } - } - } - - /** - * Deletes all the repeated ocurrences of strings in |array|. - */ - function deleteDuplicateStringsFromArray(array) { - // Build up set of each entry in array. - var seenSoFar = {}; - - for (var i = 0; i < array.length;) { - var value = array[i]; - if (seenSoFar[value]) { - array.splice(i, 1); - } else { - seenSoFar[value] = true; - i++; - } - } - } - - /** - * Builds a map out of the array |list|. - */ - function arrayToSet(list) { - var set = {}; - for (var i = 0; i < list.length; ++i) - set[list[i]] = true; - return set; - } - - function trimWhitespace(text) { - var m = /^\s*(.*)\s*$/.exec(text); - return m[1]; - } - - /** - * Selects the option in |select| which has a value of |value|. - */ - function setSelectedOptionByValue(select, value) { - for (var i = 0; i < select.options.length; ++i) { - if (select.options[i].value == value) { - select.options[i].selected = true; - return true; - } - } - return false; - } - - /** - * Adds a checkbox to |parent|. The checkbox will have a label on its right - * with text |label|. Returns the checkbox input node. - */ - function addLabeledCheckbox(parent, label) { - var labelNode = addNode(parent, 'label'); - var checkbox = addNode(labelNode, 'input'); - checkbox.type = 'checkbox'; - addText(labelNode, label); - return checkbox; - } - - /** - * Return the last component in a path which is separated by either forward - * slashes or backslashes. - */ - function getFilenameFromPath(path) { - var lastSlash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); - if (lastSlash == -1) - return path; - - return path.substr(lastSlash + 1); - } - - /** - * Returns the current time in milliseconds since unix epoch. - */ - function getTimeMillis() { - return (new Date()).getTime(); - } - - /** - * Toggle a node between hidden/invisible. - */ - function toggleNodeDisplay(n) { - if (n.style.display == '') { - n.style.display = 'none'; - } else { - n.style.display = ''; - } - } - - /** - * Set the visibility state of a node. - */ - function setNodeDisplay(n, visible) { - if (visible) { - n.style.display = ''; - } else { - n.style.display = 'none'; - } - } - - // -------------------------------------------------------------------------- - // Functions that augment, bucket, and compute aggregates for the input data. - // -------------------------------------------------------------------------- - - /** - * Adds new derived properties to row. Mutates the provided dictionary |e|. - */ - function augmentDataRow(e) { - computeDataRowAverages(e); - e[KEY_SOURCE_LOCATION] = e[KEY_FILE_NAME] + ' [' + e[KEY_LINE_NUMBER] + ']'; - } - - function computeDataRowAverages(e) { - e[KEY_AVG_QUEUE_TIME] = e[KEY_QUEUE_TIME] / e[KEY_COUNT]; - e[KEY_AVG_RUN_TIME] = e[KEY_RUN_TIME] / e[KEY_COUNT]; - - if (loadTimeData.getBoolean('enableMemoryTaskProfiler')) { - e[KEY_MEMORY_AVG_ALLOC_OPS] = e[KEY_MEMORY_ALLOC_OPS] / e[KEY_COUNT]; - e[KEY_MEMORY_AVG_FREE_OPS] = e[KEY_MEMORY_FREE_OPS] / e[KEY_COUNT]; - e[KEY_MEMORY_AVG_NET_BYTES] = - (e[KEY_MEMORY_ALLOCATED_BYTES] - e[KEY_MEMORY_FREED_BYTES]) / - e[KEY_COUNT]; - } - } - - /** - * Creates and initializes an aggregator object for each key in |columns|. - * Returns an array whose keys are values from |columns|, and whose - * values are Aggregator instances. - */ - function initializeAggregates(columns) { - var aggregates = []; - - for (var i = 0; i < columns.length; ++i) { - var key = columns[i]; - var aggregatorFactory = KEY_PROPERTIES[key].aggregator; - aggregates[key] = aggregatorFactory.create(key); - } - - return aggregates; - } - - function consumeAggregates(aggregates, row) { - for (var key in aggregates) - aggregates[key].consume(row); - } - - function bucketIdenticalRows(rows, identityKeys, propertyGetterFunc) { - var identicalRows = {}; - for (var i = 0; i < rows.length; ++i) { - var r = rows[i]; - - var rowIdentity = []; - for (var j = 0; j < identityKeys.length; ++j) - rowIdentity.push(propertyGetterFunc(r, identityKeys[j])); - rowIdentity = rowIdentity.join('\n'); - - var l = identicalRows[rowIdentity]; - if (!l) { - l = []; - identicalRows[rowIdentity] = l; - } - l.push(r); - } - return identicalRows; - } - - /** - * Merges the rows in |origRows|, by collapsing the columns listed in - * |mergeKeys|. Returns an array with the merged rows (in no particular - * order). - * - * If |mergeSimilarThreads| is true, then threads with a similar name will be - * considered equivalent. For instance, "WorkerThread-1" and "WorkerThread-2" - * will be remapped to "WorkerThread-*". - * - * If |outputAsDictionary| is false then the merged rows will be returned as a - * flat list. Otherwise the result will be a dictionary, where each row - * has a unique key. - */ - function mergeRows( - origRows, mergeKeys, mergeSimilarThreads, outputAsDictionary) { - // Define a translation function for each property. Normally we copy over - // properties as-is, but if we have been asked to "merge similar threads" we - // we will remap the thread names that end in a numeric suffix. - var propertyGetterFunc; - - if (mergeSimilarThreads) { - propertyGetterFunc = function(row, key) { - var value = row[key]; - // If the property is a thread name, try to remap it. - if (key == KEY_BIRTH_THREAD || key == KEY_DEATH_THREAD) { - var m = /^(.*[^\d])(\d+)$/.exec(value); - if (m) - value = m[1] + '*'; - } - return value; - }; - } else { - propertyGetterFunc = function(row, key) { - return row[key]; - }; - } - - // Determine which sets of properties a row needs to match on to be - // considered identical to another row. - var identityKeys = IDENTITY_KEYS.slice(0); - deleteValuesFromArray(identityKeys, mergeKeys); - - // Set |aggregateKeys| to everything else, since we will be aggregating - // their value as part of the merge. - var aggregateKeys = ALL_KEYS.slice(0); - deleteValuesFromArray(aggregateKeys, IDENTITY_KEYS); - deleteValuesFromArray(aggregateKeys, mergeKeys); - - // Group all the identical rows together, bucketed into |identicalRows|. - var identicalRows = - bucketIdenticalRows(origRows, identityKeys, propertyGetterFunc); - - var mergedRows = outputAsDictionary ? {} : []; - - // Merge the rows and save the results to |mergedRows|. - for (var k in identicalRows) { - // We need to smash the list |l| down to a single row... - var l = identicalRows[k]; - - var newRow = []; - - if (outputAsDictionary) { - mergedRows[k] = newRow; - } else { - mergedRows.push(newRow); - } - - // Copy over all the identity columns to the new row (since they - // were the same for each row matched). - for (var i = 0; i < identityKeys.length; ++i) - newRow[identityKeys[i]] = propertyGetterFunc(l[0], identityKeys[i]); - - // Compute aggregates for the other columns. - var aggregates = initializeAggregates(aggregateKeys); - - // Feed the rows to the aggregators. - for (var i = 0; i < l.length; ++i) - consumeAggregates(aggregates, l[i]); - - // Suck out the data generated by the aggregators. - for (var aggregateKey in aggregates) - newRow[aggregateKey] = aggregates[aggregateKey].getValue(); - } - - return mergedRows; - } - - /** - * Takes two dictionaries data1 and data2, and returns a new flat list which - * represents the difference between them. The exact meaning of "difference" - * is column specific, but for most numeric fields (like the count, or total - * time), it is found by subtracting. - * - * Rows in data1 and data2 are expected to use the same scheme for the keys. - * In other words, data1[k] is considered the analagous row to data2[k]. - */ - function subtractSnapshots(data1, data2, columnsToExclude) { - // These columns are computed from the other columns. We won't bother - // diffing/aggregating these, but rather will derive them again from the - // final row. - var COMPUTED_AGGREGATE_KEYS = [KEY_AVG_QUEUE_TIME, KEY_AVG_RUN_TIME]; - if (loadTimeData.getBoolean('enableMemoryTaskProfiler')) { - COMPUTED_AGGREGATE_KEYS = COMPUTED_AGGREGATE_KEYS.concat([ - KEY_MEMORY_AVG_ALLOC_OPS, KEY_MEMORY_AVG_FREE_OPS, - KEY_MEMORY_AVG_NET_BYTES - ]); - } - - // These are the keys which determine row equality. Since we are not doing - // any merging yet at this point, it is simply the list of all identity - // columns. - var identityKeys = IDENTITY_KEYS.slice(0); - deleteValuesFromArray(identityKeys, columnsToExclude); - - // The columns to compute via aggregation is everything else. - var aggregateKeys = ALL_KEYS.slice(0); - deleteValuesFromArray(aggregateKeys, IDENTITY_KEYS); - deleteValuesFromArray(aggregateKeys, COMPUTED_AGGREGATE_KEYS); - deleteValuesFromArray(aggregateKeys, columnsToExclude); - - var diffedRows = []; - - for (var rowId in data2) { - var row1 = data1[rowId]; - var row2 = data2[rowId]; - - var newRow = []; - - // Copy over all the identity columns to the new row (since they - // were the same for each row matched). - for (var i = 0; i < identityKeys.length; ++i) - newRow[identityKeys[i]] = row2[identityKeys[i]]; - - // Diff the two rows. - if (row1) { - for (var i = 0; i < aggregateKeys.length; ++i) { - var aggregateKey = aggregateKeys[i]; - var a = row1[aggregateKey]; - var b = row2[aggregateKey]; - - var diffFunc = KEY_PROPERTIES[aggregateKey].diff; - newRow[aggregateKey] = diffFunc(a, b); - } - } else { - // If the the row doesn't appear in snapshot1, then there is nothing to - // diff, so just copy row2 as is. - for (var i = 0; i < aggregateKeys.length; ++i) { - var aggregateKey = aggregateKeys[i]; - newRow[aggregateKey] = row2[aggregateKey]; - } - } - - if (newRow[KEY_COUNT] == 0) { - // If a row's count has gone to zero, it means there were no new - // occurrences of it in the second snapshot, so remove it. - continue; - } - - // Since we excluded the averages during the diffing phase, re-compute - // them using the diffed totals. - computeDataRowAverages(newRow); - diffedRows.push(newRow); - } - - return diffedRows; - } - - // -------------------------------------------------------------------------- - // HTML drawing code - // -------------------------------------------------------------------------- - - function getTextValueForProperty(key, value) { - if (value == undefined) { - // A value may be undefined as a result of having merging rows. We - // won't actually draw it, but this might be called by the filter. - return ''; - } - - var textPrinter = KEY_PROPERTIES[key].textPrinter; - if (textPrinter) - return textPrinter(value); - return value.toString(); - } - - /** - * Renders the property value |value| into cell |td|. The name of this - * property is |key|. - */ - function drawValueToCell(td, key, value) { - // Get a text representation of the value. - var text = getTextValueForProperty(key, value); - - // Apply the desired cell alignment. - var cellAlignment = KEY_PROPERTIES[key].cellAlignment; - if (cellAlignment) - td.align = cellAlignment; - - if (key == KEY_SOURCE_LOCATION) { - // Linkify the source column so it jumps to the source code. This doesn't - // take into account the particular code this build was compiled from, or - // local edits to source. It should however work correctly for top of tree - // builds. - var m = /^(.*) \[(\d+)\]$/.exec(text); - if (m) { - var filepath = m[1]; - var filename = getFilenameFromPath(filepath); - var linenumber = m[2]; - - var link = addNode(td, 'a', filename + ' [' + linenumber + ']'); - - link.href = 'https://code.google.com/p/chromium/codesearch#search/&q=' + - encodeURIComponent(filename) + ':' + linenumber + - '&sq=package:chromium&type=cs'; - link.target = '_blank'; - return; - } - } - - // String values can get pretty long. If the string contains no spaces, then - // CSS fails to wrap it, and it overflows the cell causing the table to get - // really big. We solve this using a hack: insert a <wbr> element after - // every single character. This will allow the rendering engine to wrap the - // value, and hence avoid it overflowing! - var kMinLengthBeforeWrap = 20; - - addText(td, text.substr(0, kMinLengthBeforeWrap)); - for (var i = kMinLengthBeforeWrap; i < text.length; ++i) { - addNode(td, 'wbr'); - addText(td, text.substr(i, 1)); - } - } - - // -------------------------------------------------------------------------- - // Helper code for handling the sort and grouping dropdowns. - // -------------------------------------------------------------------------- - - function addOptionsForGroupingSelect(select) { - // Add "no group" choice. - addNode(select, 'option', '---').value = ''; - - for (var i = 0; i < GROUPING_DROPDOWN_CHOICES.length; ++i) { - var key = GROUPING_DROPDOWN_CHOICES[i]; - var option = addNode(select, 'option', getNameForKey(key)); - option.value = key; - } - } - - function addOptionsForSortingSelect(select) { - // Add "no sort" choice. - addNode(select, 'option', '---').value = ''; - - // Add a divider. - addNode(select, 'optgroup').label = ''; - - for (var i = 0; i < SORT_DROPDOWN_CHOICES.length; ++i) { - var key = SORT_DROPDOWN_CHOICES[i]; - addNode(select, 'option', getNameForKey(key)).value = key; - } - - // Add a divider. - addNode(select, 'optgroup').label = ''; - - // Add the same options, but for descending. - for (var i = 0; i < SORT_DROPDOWN_CHOICES.length; ++i) { - var key = SORT_DROPDOWN_CHOICES[i]; - var n = addNode(select, 'option', getNameForKey(key) + ' (DESC)'); - n.value = reverseSortKey(key); - } - } - - /** - * Helper function used to update the sorting and grouping lists after a - * dropdown changes. - */ - function updateKeyListFromDropdown(list, i, select) { - // Update the list. - if (i < list.length) { - list[i] = select.value; - } else { - list.push(select.value); - } - - // Normalize the list, so setting 'none' as primary zeros out everything - // else. - for (var i = 0; i < list.length; ++i) { - if (list[i] == '') { - list.splice(i, list.length - i); - break; - } - } - } - - /** - * Comparator for property |key|, having values |value1| and |value2|. - * If the key has defined a custom comparator use it. Otherwise use a - * default "less than" comparison. - */ - function compareValuesForKey(key, value1, value2) { - var comparator = KEY_PROPERTIES[key].comparator; - if (comparator) - return comparator(value1, value2); - return simpleCompare(value1, value2); - } - - function reverseSortKey(key) { - return -key; - } - - function sortKeyIsReversed(key) { - return key < 0; - } - - function sortKeysMatch(key1, key2) { - return Math.abs(key1) == Math.abs(key2); - } - - function getKeysForCheckedBoxes(checkboxes) { - var keys = []; - for (var k in checkboxes) { - if (checkboxes[k].checked) - keys.push(k); - } - return keys; - } - - // -------------------------------------------------------------------------- - - /** - * @constructor - */ - function MainView() { - // Make sure we have a definition for each key. - for (var k = BEGIN_KEY; k < END_KEY; ++k) { - if (!KEY_PROPERTIES[k]) - throw 'KEY_PROPERTIES[] not defined for key: ' + k; - } - - this.init_(); - } - - MainView.prototype = { - addDataToSnapshot: function(data) { - // TODO(eroman): We need to know which snapshot this data belongs to! - // For now we assume it is the most recent snapshot. - var snapshotIndex = this.snapshots_.length - 1; - - var snapshot = this.snapshots_[snapshotIndex]; - - var pid = data.process_id; - var ptype = data.process_type; - - // Save the browser's representation of the data - snapshot.origData.push(data); - - // Augment each data row with the process information. - var rows = data.list; - for (var i = 0; i < rows.length; ++i) { - // Transform the data from a dictionary to an array. This internal - // representation is more compact and faster to access. - var origRow = rows[i]; - var newRow = []; - - newRow[KEY_PROCESS_ID] = pid; - newRow[KEY_PROCESS_TYPE] = ptype; - - // Copy over the known properties which have a 1:1 mapping with JSON. - for (var k = BEGIN_KEY; k < END_KEY; ++k) { - var inputJsonKey = KEY_PROPERTIES[k].inputJsonKey; - if (inputJsonKey != undefined) { - newRow[k] = getPropertyByPath(origRow, inputJsonKey); - } - } - - if (newRow[KEY_COUNT] == 0) { - // When resetting the data, it is possible for the backend to give us - // counts of "0". There is no point adding these rows (in fact they - // will cause us to do divide by zeros when calculating averages and - // stuff), so we skip past them. - continue; - } - - // Add our computed properties. - augmentDataRow(newRow); - - snapshot.flatData.push(newRow); - } - - if (!arrayToSet(this.getSelectedSnapshotIndexes_())[snapshotIndex]) { - // Optimization: If this snapshot is not a data dependency for the - // current display, then don't bother updating anything. - return; - } - - // We may end up calling addDataToSnapshot_() repeatedly (once for each - // process). To avoid this from slowing us down we do bulk updates on a - // timer. - this.updateMergedDataSoon_(); - }, - - updateMergedDataSoon_: function() { - if (this.updateMergedDataPending_) { - // If a delayed task has already been posted to re-merge the data, - // then we don't need to do anything extra. - return; - } - - // Otherwise schedule updateMergedData_() to be called later. We want it - // to be called no more than once every PROCESS_DATA_DELAY_MS - // milliseconds. - - if (this.lastUpdateMergedDataTime_ == undefined) - this.lastUpdateMergedDataTime_ = 0; - - var timeSinceLastMerge = getTimeMillis() - this.lastUpdateMergedDataTime_; - var timeToWait = Math.max(0, PROCESS_DATA_DELAY_MS - timeSinceLastMerge); - - var functionToRun = function() { - // Do the actual update. - this.updateMergedData_(); - // Keep track of when we last ran. - this.lastUpdateMergedDataTime_ = getTimeMillis(); - this.updateMergedDataPending_ = false; - }.bind(this); - - this.updateMergedDataPending_ = true; - window.setTimeout(functionToRun, timeToWait); - }, - - /** - * Returns a list of the currently selected snapshots. This list is - * guaranteed to be of length 1 or 2. - */ - getSelectedSnapshotIndexes_: function() { - var indexes = this.getSelectedSnapshotBoxes_(); - for (var i = 0; i < indexes.length; ++i) - indexes[i] = indexes[i].__index; - return indexes; - }, - - /** - * Same as getSelectedSnapshotIndexes_(), only it returns the actual - * checkbox input DOM nodes rather than the snapshot ID. - */ - getSelectedSnapshotBoxes_: function() { - // Figure out which snapshots to use for our data. - var boxes = []; - for (var i = 0; i < this.snapshots_.length; ++i) { - var box = this.getSnapshotCheckbox_(i); - if (box.checked) - boxes.push(box); - } - return boxes; - }, - - /** - * Re-draw the description that explains which snapshots are currently - * selected (if two snapshots were selected we explain that the *difference* - * between them is being displayed). - */ - updateSnapshotSelectionSummaryDiv_: function() { - var summaryDiv = $(SNAPSHOT_SELECTION_SUMMARY_ID); - - var selectedSnapshots = this.getSelectedSnapshotIndexes_(); - if (selectedSnapshots.length == 0) { - // This can occur during an attempt to load a file or following file - // load failure. We just ignore it and move on. - } else if (selectedSnapshots.length == 1) { - // If only one snapshot is chosen then we will display that snapshot's - // data in its entirety. - this.flatData_ = this.snapshots_[selectedSnapshots[0]].flatData; - - // Don't bother displaying any text when just 1 snapshot is selected, - // since it is obvious what this should do. - summaryDiv.innerText = ''; - } else if (selectedSnapshots.length == 2) { - // Otherwise if two snapshots were chosen, show the difference between - // them. - var snapshot1 = this.snapshots_[selectedSnapshots[0]]; - var snapshot2 = this.snapshots_[selectedSnapshots[1]]; - - var timeDeltaInSeconds = - ((snapshot2.time - snapshot1.time) / 1000).toFixed(0); - - // Explain that what is being shown is the difference between two - // snapshots. - summaryDiv.innerText = 'Showing the difference between snapshots #' + - selectedSnapshots[0] + ' and #' + selectedSnapshots[1] + ' (' + - timeDeltaInSeconds + ' seconds worth of data)'; - } else { - // This shouldn't be possible... - throw 'Unexpected number of selected snapshots'; - } - }, - - updateMergedData_: function() { - // Retrieve the merge options. - var mergeColumns = this.getMergeColumns_(); - var shouldMergeSimilarThreads = this.shouldMergeSimilarThreads_(); - - var selectedSnapshots = this.getSelectedSnapshotIndexes_(); - - // We do merges a bit differently depending if we are displaying the diffs - // between two snapshots, or just displaying a single snapshot. - if (selectedSnapshots.length == 1) { - var snapshot = this.snapshots_[selectedSnapshots[0]]; - this.mergedData_ = mergeRows( - snapshot.flatData, mergeColumns, shouldMergeSimilarThreads, false); - - } else if (selectedSnapshots.length == 2) { - var snapshot1 = this.snapshots_[selectedSnapshots[0]]; - var snapshot2 = this.snapshots_[selectedSnapshots[1]]; - - // Merge the data for snapshot1. - var mergedRows1 = mergeRows( - snapshot1.flatData, mergeColumns, shouldMergeSimilarThreads, true); - - // Merge the data for snapshot2. - var mergedRows2 = mergeRows( - snapshot2.flatData, mergeColumns, shouldMergeSimilarThreads, true); - - // Do a diff between the two snapshots. - this.mergedData_ = - subtractSnapshots(mergedRows1, mergedRows2, mergeColumns); - } else { - throw 'Unexpected number of selected snapshots'; - } - - // Recompute filteredData_ (since it is derived from mergedData_) - this.updateFilteredData_(); - }, - - updateFilteredData_: function() { - // Recompute filteredData_. - this.filteredData_ = []; - var filterFunc = this.getFilterFunction_(); - for (var i = 0; i < this.mergedData_.length; ++i) { - var r = this.mergedData_[i]; - if (!filterFunc(r)) { - // Not matched by our filter, discard. - continue; - } - this.filteredData_.push(r); - } - - // Recompute groupedData_ (since it is derived from filteredData_) - this.updateGroupedData_(); - }, - - updateGroupedData_: function() { - // Recompute groupedData_. - var groupKeyToData = {}; - var entryToGroupKeyFunc = this.getGroupingFunction_(); - for (var i = 0; i < this.filteredData_.length; ++i) { - var r = this.filteredData_[i]; - - var groupKey = entryToGroupKeyFunc(r); - - var groupData = groupKeyToData[groupKey]; - if (!groupData) { - groupData = { - key: JSON.parse(groupKey), - aggregates: initializeAggregates(ALL_KEYS), - rows: [], - }; - groupKeyToData[groupKey] = groupData; - } - - // Add the row to our list. - groupData.rows.push(r); - - // Update aggregates for each column. - consumeAggregates(groupData.aggregates, r); - } - this.groupedData_ = groupKeyToData; - - // Figure out a display order for the groups themselves. - this.sortedGroupKeys_ = getDictionaryKeys(groupKeyToData); - this.sortedGroupKeys_.sort(this.getGroupSortingFunction_()); - - // Sort the group data. - this.sortGroupedData_(); - }, - - sortGroupedData_: function() { - var sortingFunc = this.getSortingFunction_(); - for (var k in this.groupedData_) - this.groupedData_[k].rows.sort(sortingFunc); - - // Every cached data dependency is now up to date, all that is left is - // to actually draw the result. - this.redrawData_(); - }, - - getVisibleColumnKeys_: function() { - // Figure out what columns to include, based on the selected checkboxes. - var columns = this.getSelectionColumns_(); - columns = columns.slice(0); - - // Eliminate columns which we are merging on. - deleteValuesFromArray(columns, this.getMergeColumns_()); - - // Eliminate columns which we are grouped on. - if (this.sortedGroupKeys_.length > 0) { - // The grouping will be the the same for each so just pick the first. - var randomGroupKey = this.groupedData_[this.sortedGroupKeys_[0]].key; - - // The grouped properties are going to be the same for each row in our, - // table, so avoid drawing them in our table! - var keysToExclude = []; - - for (var i = 0; i < randomGroupKey.length; ++i) - keysToExclude.push(randomGroupKey[i].key); - deleteValuesFromArray(columns, keysToExclude); - } - - // If we are currently showing a "diff", hide the max columns, since we - // are not populating it correctly. See the TODO at the top of this file. - if (this.getSelectedSnapshotIndexes_().length > 1) - deleteValuesFromArray(columns, [KEY_MAX_RUN_TIME, KEY_MAX_QUEUE_TIME]); - - return columns; - }, - - redrawData_: function() { - // Clear the results div, sine we may be overwriting older data. - var parent = $(RESULTS_DIV_ID); - parent.innerHTML = ''; - - var columns = this.getVisibleColumnKeys_(); - - // Draw each group. - for (var i = 0; i < this.sortedGroupKeys_.length; ++i) { - var k = this.sortedGroupKeys_[i]; - this.drawGroup_(parent, k, columns); - } - }, - - /** - * Renders the information for a particular group. - */ - drawGroup_: function(parent, groupKey, columns) { - var groupData = this.groupedData_[groupKey]; - - var div = addNode(parent, 'div'); - div.className = 'group-container'; - - this.drawGroupTitle_(div, groupData.key); - - var table = addNode(div, 'table'); - - this.drawDataTable_(table, groupData, columns, groupKey); - }, - - /** - * Draws a title into |parent| that describes |groupKey|. - */ - drawGroupTitle_: function(parent, groupKey) { - if (groupKey.length == 0) { - // Empty group key means there was no grouping. - return; - } - - var parent = addNode(parent, 'div'); - parent.className = 'group-title-container'; - - // Each component of the group key represents the "key=value" constraint - // for this group. Show these as an AND separated list. - for (var i = 0; i < groupKey.length; ++i) { - if (i > 0) - addNode(parent, 'i', ' and '); - var e = groupKey[i]; - addNode(parent, 'b', getNameForKey(e.key) + ' = '); - addNode(parent, 'span', e.value); - } - }, - - /** - * Renders a table which summarizes all |column| fields for |data|. - */ - drawDataTable_: function(table, data, columns, groupKey) { - table.className = 'results-table'; - var thead = addNode(table, 'thead'); - var tbody = addNode(table, 'tbody'); - - var displaySettings = this.getGroupDisplaySettings_(groupKey); - var limit = displaySettings.limit; - - this.drawAggregateRow_(thead, data.aggregates, columns); - this.drawTableHeader_(thead, columns); - this.drawTableBody_(tbody, data.rows, columns, limit); - this.drawTruncationRow_( - tbody, data.rows.length, limit, columns.length, groupKey); - }, - - drawTableHeader_: function(thead, columns) { - var tr = addNode(thead, 'tr'); - for (var i = 0; i < columns.length; ++i) { - var key = columns[i]; - var th = addNode(tr, 'th', getNameForKey(key)); - th.onclick = this.onClickColumn_.bind(this, key); - - // Draw an indicator if we are currently sorted on this column. - // TODO(eroman): Should use an icon instead of asterisk! - for (var j = 0; j < this.currentSortKeys_.length; ++j) { - if (sortKeysMatch(this.currentSortKeys_[j], key)) { - var sortIndicator = addNode(th, 'span', '*'); - sortIndicator.style.color = 'red'; - if (sortKeyIsReversed(this.currentSortKeys_[j])) { - // Use double-asterisk for descending columns. - addText(sortIndicator, '*'); - } - break; - } - } - } - }, - - drawTableBody_: function(tbody, rows, columns, limit) { - for (var i = 0; i < rows.length && i < limit; ++i) { - var e = rows[i]; - - var tr = addNode(tbody, 'tr'); - - for (var c = 0; c < columns.length; ++c) { - var key = columns[c]; - var value = e[key]; - - var td = addNode(tr, 'td'); - drawValueToCell(td, key, value); - } - } - }, - - /** - * Renders a row that describes all the aggregate values for |columns|. - */ - drawAggregateRow_: function(tbody, aggregates, columns) { - var tr = addNode(tbody, 'tr'); - tr.className = 'aggregator-row'; - - for (var i = 0; i < columns.length; ++i) { - var key = columns[i]; - var td = addNode(tr, 'td'); - - // Most of our outputs are numeric, so we want to align them to the - // right. However for the unique counts we will center. - if (KEY_PROPERTIES[key].aggregator == UniquifyAggregator) { - td.align = 'center'; - } else { - td.align = 'right'; - } - - var aggregator = aggregates[key]; - if (aggregator) - td.innerText = aggregator.getValueAsText(); - } - }, - - /** - * Renders a row which describes how many rows the table has, how many are - * currently hidden, and a set of buttons to show more. - */ - drawTruncationRow_: function(tbody, numRows, limit, numColumns, groupKey) { - var numHiddenRows = Math.max(numRows - limit, 0); - var numVisibleRows = numRows - numHiddenRows; - - var tr = addNode(tbody, 'tr'); - tr.className = 'truncation-row'; - var td = addNode(tr, 'td'); - td.colSpan = numColumns; - - addText(td, numRows + ' rows'); - if (numHiddenRows > 0) { - var s = addNode(td, 'span', ' (' + numHiddenRows + ' hidden) '); - s.style.color = 'red'; - } - - if (numVisibleRows > LIMIT_INCREMENT) { - addNode(td, 'button', 'Show less').onclick = - this.changeGroupDisplayLimit_.bind( - this, groupKey, -LIMIT_INCREMENT); - } - if (numVisibleRows > 0) { - addNode(td, 'button', 'Show none').onclick = - this.changeGroupDisplayLimit_.bind(this, groupKey, -Infinity); - } - - if (numHiddenRows > 0) { - addNode(td, 'button', 'Show more').onclick = - this.changeGroupDisplayLimit_.bind(this, groupKey, LIMIT_INCREMENT); - addNode(td, 'button', 'Show all').onclick = - this.changeGroupDisplayLimit_.bind(this, groupKey, Infinity); - } - }, - - /** - * Adjusts the row limit for group |groupKey| by |delta|. - */ - changeGroupDisplayLimit_: function(groupKey, delta) { - // Get the current settings for this group. - var settings = this.getGroupDisplaySettings_(groupKey, true); - - // Compute the adjusted limit. - var newLimit = settings.limit; - var totalNumRows = this.groupedData_[groupKey].rows.length; - newLimit = Math.min(totalNumRows, newLimit); - newLimit += delta; - newLimit = Math.max(0, newLimit); - - // Update the settings with the new limit. - settings.limit = newLimit; - - // TODO(eroman): It isn't necessary to redraw *all* the data. Really we - // just need to insert the missing rows (everything else stays the same)! - this.redrawData_(); - }, - - /** - * Returns the rendering settings for group |groupKey|. This includes things - * like how many rows to display in the table. - */ - getGroupDisplaySettings_: function(groupKey, opt_create) { - var settings = this.groupDisplaySettings_[groupKey]; - if (!settings) { - // If we don't have any settings for this group yet, create some - // default ones. - if (groupKey == '[]') { - // (groupKey of '[]' is what we use for ungrouped data). - settings = {limit: INITIAL_UNGROUPED_ROW_LIMIT}; - } else { - settings = {limit: INITIAL_GROUP_ROW_LIMIT}; - } - if (opt_create) - this.groupDisplaySettings_[groupKey] = settings; - } - return settings; - }, - - init_: function() { - this.snapshots_ = []; - - // Start fetching the data from the browser; this will be our snapshot #0. - this.takeSnapshot_(); - - // Data goes through the following pipeline: - // (1) Raw data received from browser, and transformed into our own - // internal row format (where properties are indexed by KEY_* - // constants.) - // (2) We "augment" each row by adding some extra computed columns - // (like averages). - // (3) The rows are merged using current merge settings. - // (4) The rows that don't match current search expression are - // tossed out. - // (5) The rows are organized into "groups" based on current settings, - // and aggregate values are computed for each resulting group. - // (6) The rows within each group are sorted using current settings. - // (7) The grouped rows are drawn to the screen. - this.mergedData_ = []; - this.filteredData_ = []; - this.groupedData_ = {}; - this.sortedGroupKeys_ = []; - - this.groupDisplaySettings_ = {}; - - this.fillSelectionCheckboxes_($(COLUMN_TOGGLES_CONTAINER_ID)); - this.fillMergeCheckboxes_($(COLUMN_MERGE_TOGGLES_CONTAINER_ID)); - - $(FILTER_SEARCH_ID).onsearch = this.onChangedFilter_.bind(this); - - this.currentSortKeys_ = INITIAL_SORT_KEYS.slice(0); - this.currentGroupingKeys_ = INITIAL_GROUP_KEYS.slice(0); - - this.fillGroupingDropdowns_(); - this.fillSortingDropdowns_(); - - $(EDIT_COLUMNS_LINK_ID).onclick = - toggleNodeDisplay.bind(null, $(EDIT_COLUMNS_ROW)); - - $(TOGGLE_SNAPSHOTS_LINK_ID).onclick = - toggleNodeDisplay.bind(null, $(SNAPSHOTS_ROW)); - - $(MERGE_SIMILAR_THREADS_CHECKBOX_ID).onchange = - this.onMergeSimilarThreadsCheckboxChanged_.bind(this); - - $(TAKE_SNAPSHOT_BUTTON_ID).onclick = this.takeSnapshot_.bind(this); - - $(SAVE_SNAPSHOTS_BUTTON_ID).onclick = this.saveSnapshots_.bind(this); - $(SNAPSHOT_FILE_LOADER_ID).onchange = this.loadFileChanged_.bind(this); - }, - - takeSnapshot_: function() { - // Start a new empty snapshot. Make note of the current time, so we know - // when the snapshot was taken. - this.snapshots_.push({flatData: [], origData: [], time: getTimeMillis()}); - - // Update the UI to reflect the new snapshot. - this.addSnapshotToList_(this.snapshots_.length - 1); - - // Ask the browser for the profiling data. We will receive the data - // later through a callback to addDataToSnapshot_(). - g_browserBridge.sendGetData(); - }, - - saveSnapshots_: function() { - var snapshots = []; - for (var i = 0; i < this.snapshots_.length; ++i) { - snapshots.push({ - data: this.snapshots_[i].origData, - timestamp: Math.floor(this.snapshots_[i].time / 1000) - }); - } - - var dump = { - 'userAgent': navigator.userAgent, - 'version': 1, - 'snapshots': snapshots - }; - - var dumpText = JSON.stringify(dump, null, ' '); - var textBlob = - new Blob([dumpText], {type: 'octet/stream', endings: 'native'}); - var blobUrl = window.URL.createObjectURL(textBlob); - $(DOWNLOAD_ANCHOR_ID).href = blobUrl; - $(DOWNLOAD_ANCHOR_ID).click(); - }, - - loadFileChanged_: function() { - this.loadSnapshots_($(SNAPSHOT_FILE_LOADER_ID).files[0]); - }, - - loadSnapshots_: function(file) { - if (file) { - var fileReader = new FileReader(); - - fileReader.onload = this.onLoadSnapshotsFile_.bind(this, file); - fileReader.onerror = this.onLoadSnapshotsFileError_.bind(this, file); - - fileReader.readAsText(file); - } - }, - - onLoadSnapshotsFile_: function(file, event) { - try { - var parsed = null; - parsed = JSON.parse(event.target.result); - - if (parsed.version != 1) { - throw new Error('Unrecognized version: ' + parsed.version); - } - - if (parsed.snapshots.length < 1) { - throw new Error('File contains no data'); - } - - this.displayLoadedFile_(file, parsed); - this.hideFileLoadError_(); - } catch (error) { - this.displayFileLoadError_('File load failure: ' + error.message); - } - }, - - clearExistingSnapshots_: function() { - var tbody = $('snapshots-tbody'); - this.snapshots_ = []; - tbody.innerHTML = ''; - this.updateMergedDataSoon_(); - }, - - displayLoadedFile_: function(file, content) { - this.clearExistingSnapshots_(); - $(TAKE_SNAPSHOT_BUTTON_ID).disabled = true; - $(SAVE_SNAPSHOTS_BUTTON_ID).disabled = true; - - if (content.snapshots.length > 1) { - setNodeDisplay($(SNAPSHOTS_ROW), true); - } - - for (var i = 0; i < content.snapshots.length; ++i) { - var snapshot = content.snapshots[i]; - this.snapshots_.push( - {flatData: [], origData: [], time: snapshot.timestamp * 1000}); - this.addSnapshotToList_(this.snapshots_.length - 1); - var snapshotData = snapshot.data; - for (var j = 0; j < snapshotData.length; ++j) { - this.addDataToSnapshot(snapshotData[j]); - } - } - this.redrawData_(); - }, - - onLoadSnapshotsFileError_: function(file, filedata) { - this.displayFileLoadError_('Error loading ' + file.name); - }, - - displayFileLoadError_: function(message) { - $(LOAD_ERROR_ID).textContent = message; - $(LOAD_ERROR_ID).hidden = false; - }, - - hideFileLoadError_: function() { - $(LOAD_ERROR_ID).textContent = ''; - $(LOAD_ERROR_ID).hidden = true; - }, - - getSnapshotCheckbox_: function(i) { - return $(this.getSnapshotCheckboxId_(i)); - }, - - getSnapshotCheckboxId_: function(i) { - return 'snapshotCheckbox-' + i; - }, - - addSnapshotToList_: function(i) { - var tbody = $('snapshots-tbody'); - - var tr = addNode(tbody, 'tr'); - - var id = this.getSnapshotCheckboxId_(i); - - var checkboxCell = addNode(tr, 'td'); - var checkbox = addNode(checkboxCell, 'input'); - checkbox.type = 'checkbox'; - checkbox.id = id; - checkbox.__index = i; - checkbox.onclick = this.onSnapshotCheckboxChanged_.bind(this); - - addNode(tr, 'td', '#' + i); - - var labelCell = addNode(tr, 'td'); - var l = addNode(labelCell, 'label'); - - var dateString = new Date(this.snapshots_[i].time).toLocaleString(); - addText(l, dateString); - l.htmlFor = id; - - // If we are on snapshot 0, make it the default. - if (i == 0) { - checkbox.checked = true; - checkbox.__time = getTimeMillis(); - this.updateSnapshotCheckboxStyling_(); - } - }, - - updateSnapshotCheckboxStyling_: function() { - for (var i = 0; i < this.snapshots_.length; ++i) { - var checkbox = this.getSnapshotCheckbox_(i); - checkbox.parentNode.parentNode.className = - checkbox.checked ? 'selected-snapshot' : ''; - } - }, - - onSnapshotCheckboxChanged_: function(event) { - // Keep track of when we clicked this box (for when we need to uncheck - // older boxes). - event.target.__time = getTimeMillis(); - - // Find all the checked boxes. Either 1 or 2 can be checked. If a third - // was just checked, then uncheck one of the earlier ones so we only have - // 2. - var checked = this.getSelectedSnapshotBoxes_(); - checked.sort(function(a, b) { - return b.__time - a.__time; - }); - if (checked.length > 2) { - for (var i = 2; i < checked.length; ++i) - checked[i].checked = false; - checked.length = 2; - } - - // We should always have at least 1 selection. Prevent the user from - // unselecting the final box. - if (checked.length == 0) - event.target.checked = true; - - this.updateSnapshotCheckboxStyling_(); - this.updateSnapshotSelectionSummaryDiv_(); - - // Recompute mergedData_ (since it is derived from selected snapshots). - this.updateMergedData_(); - }, - - fillSelectionCheckboxes_: function(parent) { - this.selectionCheckboxes_ = {}; - - var onChangeFunc = this.onSelectCheckboxChanged_.bind(this); - - for (var i = 0; i < ALL_TABLE_COLUMNS.length; ++i) { - var key = ALL_TABLE_COLUMNS[i]; - var checkbox = addLabeledCheckbox(parent, getNameForKey(key)); - checkbox.checked = true; - checkbox.onchange = onChangeFunc; - addText(parent, ' '); - this.selectionCheckboxes_[key] = checkbox; - } - - for (var i = 0; i < INITIALLY_HIDDEN_KEYS.length; ++i) { - this.selectionCheckboxes_[INITIALLY_HIDDEN_KEYS[i]].checked = false; - } - }, - - getSelectionColumns_: function() { - return getKeysForCheckedBoxes(this.selectionCheckboxes_); - }, - - getMergeColumns_: function() { - return getKeysForCheckedBoxes(this.mergeCheckboxes_); - }, - - shouldMergeSimilarThreads_: function() { - return $(MERGE_SIMILAR_THREADS_CHECKBOX_ID).checked; - }, - - fillMergeCheckboxes_: function(parent) { - this.mergeCheckboxes_ = {}; - - var onChangeFunc = this.onMergeCheckboxChanged_.bind(this); - - for (var i = 0; i < MERGEABLE_KEYS.length; ++i) { - var key = MERGEABLE_KEYS[i]; - var checkbox = addLabeledCheckbox(parent, getNameForKey(key)); - checkbox.onchange = onChangeFunc; - addText(parent, ' '); - this.mergeCheckboxes_[key] = checkbox; - } - - for (var i = 0; i < INITIALLY_MERGED_KEYS.length; ++i) { - this.mergeCheckboxes_[INITIALLY_MERGED_KEYS[i]].checked = true; - } - }, - - fillGroupingDropdowns_: function() { - var parent = $(GROUP_BY_CONTAINER_ID); - parent.innerHTML = ''; - - for (var i = 0; i <= this.currentGroupingKeys_.length; ++i) { - // Add a dropdown. - var select = addNode(parent, 'select'); - select.onchange = this.onChangedGrouping_.bind(this, select, i); - - addOptionsForGroupingSelect(select); - - if (i < this.currentGroupingKeys_.length) { - var key = this.currentGroupingKeys_[i]; - setSelectedOptionByValue(select, key); - } - } - }, - - fillSortingDropdowns_: function() { - var parent = $(SORT_BY_CONTAINER_ID); - parent.innerHTML = ''; - - for (var i = 0; i <= this.currentSortKeys_.length; ++i) { - // Add a dropdown. - var select = addNode(parent, 'select'); - select.onchange = this.onChangedSorting_.bind(this, select, i); - - addOptionsForSortingSelect(select); - - if (i < this.currentSortKeys_.length) { - var key = this.currentSortKeys_[i]; - setSelectedOptionByValue(select, key); - } - } - }, - - onChangedGrouping_: function(select, i) { - updateKeyListFromDropdown(this.currentGroupingKeys_, i, select); - this.fillGroupingDropdowns_(); - this.updateGroupedData_(); - }, - - onChangedSorting_: function(select, i) { - updateKeyListFromDropdown(this.currentSortKeys_, i, select); - this.fillSortingDropdowns_(); - this.sortGroupedData_(); - }, - - onSelectCheckboxChanged_: function() { - this.redrawData_(); - }, - - onMergeCheckboxChanged_: function() { - this.updateMergedData_(); - }, - - onMergeSimilarThreadsCheckboxChanged_: function() { - this.updateMergedData_(); - }, - - onChangedFilter_: function() { - this.updateFilteredData_(); - }, - - /** - * When left-clicking a column, change the primary sort order to that - * column. If we were already sorted on that column then reverse the order. - * - * When alt-clicking, add a secondary sort column. Similarly, if - * alt-clicking a column which was already being sorted on, reverse its - * order. - */ - onClickColumn_: function(key, event) { - // If this property wants to start off in descending order rather then - // ascending, flip it. - if (KEY_PROPERTIES[key].sortDescending) - key = reverseSortKey(key); - - // Scan through our sort order and see if we are already sorted on this - // key. If so, reverse that sort ordering. - var foundIndex = -1; - for (var i = 0; i < this.currentSortKeys_.length; ++i) { - var curKey = this.currentSortKeys_[i]; - if (sortKeysMatch(curKey, key)) { - this.currentSortKeys_[i] = reverseSortKey(curKey); - foundIndex = i; - break; - } - } - - if (event.altKey) { - if (foundIndex == -1) { - // If we weren't already sorted on the column that was alt-clicked, - // then add it to our sort. - this.currentSortKeys_.push(key); - } - } else { - if (foundIndex != 0 || - !sortKeysMatch(this.currentSortKeys_[foundIndex], key)) { - // If the column we left-clicked wasn't already our primary column, - // make it so. - this.currentSortKeys_ = [key]; - } else { - // If the column we left-clicked was already our primary column (and - // we just reversed it), remove any secondary sorts. - this.currentSortKeys_.length = 1; - } - } - - this.fillSortingDropdowns_(); - this.sortGroupedData_(); - }, - - getSortingFunction_: function() { - var sortKeys = this.currentSortKeys_.slice(0); - - // Eliminate the empty string keys (which means they were unspecified). - deleteValuesFromArray(sortKeys, ['']); - - // If no sort is specified, use our default sort. - if (sortKeys.length == 0) - sortKeys = [DEFAULT_SORT_KEYS]; - - return function(a, b) { - for (var i = 0; i < sortKeys.length; ++i) { - var key = Math.abs(sortKeys[i]); - var factor = sortKeys[i] < 0 ? -1 : 1; - - var propA = a[key]; - var propB = b[key]; - - var comparison = compareValuesForKey(key, propA, propB); - comparison *= factor; // Possibly reverse the ordering. - - if (comparison != 0) - return comparison; - } - - // Tie breaker. - return simpleCompare(JSON.stringify(a), JSON.stringify(b)); - }; - }, - - getGroupSortingFunction_: function() { - return function(a, b) { - var groupKey1 = JSON.parse(a); - var groupKey2 = JSON.parse(b); - - for (var i = 0; i < groupKey1.length; ++i) { - var comparison = compareValuesForKey( - groupKey1[i].key, groupKey1[i].value, groupKey2[i].value); - - if (comparison != 0) - return comparison; - } - - // Tie breaker. - return simpleCompare(a, b); - }; - }, - - getFilterFunction_: function() { - var searchStr = $(FILTER_SEARCH_ID).value; - - // Normalize the search expression. - searchStr = trimWhitespace(searchStr); - searchStr = searchStr.toLowerCase(); - - return function(x) { - // Match everything when there was no filter. - if (searchStr == '') - return true; - - // Treat the search text as a LOWERCASE substring search. - for (var k = BEGIN_KEY; k < END_KEY; ++k) { - var propertyText = getTextValueForProperty(k, x[k]); - if (propertyText.toLowerCase().indexOf(searchStr) != -1) - return true; - } - - return false; - }; - }, - - getGroupingFunction_: function() { - var groupings = this.currentGroupingKeys_.slice(0); - - // Eliminate the empty string groupings (which means they were - // unspecified). - deleteValuesFromArray(groupings, ['']); - - // Eliminate duplicate primary/secondary group by directives, since they - // are redundant. - deleteDuplicateStringsFromArray(groupings); - - return function(e) { - var groupKey = []; - - for (var i = 0; i < groupings.length; ++i) { - var entry = {key: groupings[i], value: e[groupings[i]]}; - groupKey.push(entry); - } - - return JSON.stringify(groupKey); - }; - }, - }; - - return MainView; -})(); diff --git a/chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb b/chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb index 5f61c4745d6..4fd285afc89 100644 --- a/chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb +++ b/chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb @@ -8,7 +8,7 @@ ## ## Top level settings ## -version_id: 12 +version_id: 13 sampled_ping_probability: 0.01 default_file_type { uma_value: 18 @@ -2217,6 +2217,36 @@ file_types { } } file_types { + # Similar to "html". Added for https://crbug.com/762702 + extension: "xht" + uma_value: 286 + ping_setting: FULL_PING + platform_settings { + danger_level: NOT_DANGEROUS + auto_open_hint: ALLOW_AUTO_OPEN + } +} +file_types { + # Similar to "html". Added for https://crbug.com/762702 + extension: "xhtm" + uma_value: 287 + ping_setting: FULL_PING + platform_settings { + danger_level: NOT_DANGEROUS + auto_open_hint: ALLOW_AUTO_OPEN + } +} +file_types { + # Similar to "html". Added for https://crbug.com/762702 + extension: "xhtml" + uma_value: 288 + ping_setting: FULL_PING + platform_settings { + danger_level: NOT_DANGEROUS + auto_open_hint: ALLOW_AUTO_OPEN + } +} +file_types { # Microsoft Exchange Public Folder Shortcut extension: "xnk" uma_value: 127 diff --git a/chromium/chrome/browser/resources/settings/BUILD.gn b/chromium/chrome/browser/resources/settings/BUILD.gn index af4bf1d57d2..cd940c9f689 100644 --- a/chromium/chrome/browser/resources/settings/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/BUILD.gn @@ -1,11 +1,11 @@ -import("../vulcanize.gni") +import("../optimize_webui.gni") import("//tools/grit/grit_rule.gni") import("//chrome/common/features.gni") settings_pak_file = "settings_resources.pak" unpak_folder = "settings_resources.unpak" -vulcanize("build") { +optimize_webui("build") { host = "settings" html_in_files = [ "settings.html", @@ -15,7 +15,7 @@ vulcanize("build") { "vulcanized.html", "lazy_load.vulcanized.html", ] - insert_in_head = "<base href=\"chrome://\$i18n{hostname}\">" + insert_in_head = "<base href=\"chrome://settings\">" input = rebase_path("$target_gen_dir/$unpak_folder", root_build_dir) js_out_files = [ "crisper.js", 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 903457b27f5..bc42399d48f 100644 --- a/chromium/chrome/browser/resources/settings/about_page/about_page.html +++ b/chromium/chrome/browser/resources/settings/about_page/about_page.html @@ -22,6 +22,7 @@ <link rel="import" href="detailed_build_info.html"> <link rel="import" href="update_warning_dialog.html"> <link rel="import" href="../settings_page/settings_subpage.html"> +<link rel="import" href="../reset_page/powerwash_dialog.html"> </if> <if expr="_google_chrome and is_macosx"> @@ -142,6 +143,25 @@ </if> </span> </div> +<if expr="chromeos"> + <div id="aboutTPMFirmwareUpdate" class="settings-box two-line" + hidden$="[[!showTPMFirmwareUpdateLineItem_]]" + on-tap="onTPMFirmwareUpdateTap_" actionable> + <div class="start"> + <div>$i18n{aboutTPMFirmwareUpdateTitle}</div> + <div class="secondary"> + $i18n{aboutTPMFirmwareUpdateDescription} + <a href="$i18n{aboutTPMFirmwareUpdateLearnMoreURL}" + target="_blank" on-tap="onLearnMoreTap_"> + $i18n{learnMore} + </a> + </div> + </div> + <button class="subpage-arrow" is="paper-icon-button-light" + aria-labelledby="aboutTPMFirmwareUpdate"> + </button> + </div> +</if> <if expr="_google_chrome and is_macosx"> <template is="dom-if" if="[[!promoteUpdaterStatus_.hidden]]"> <div id="promoteUpdater" class="settings-box" @@ -229,6 +249,12 @@ on-close="onUpdateWarningDialogClose_"> </settings-update-warning-dialog> </template> + <template is="dom-if" if="[[showTPMFirmwareUpdateDialog_]]" + restamp> + <settings-powerwash-dialog request-tpm-firmware-update + on-close="onPowerwashDialogClose_"> + </settings-powerwash-dialog> + </template> </if> </template> <script src="about_page.js"></script> 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 4270abf92a8..5ddfb82ce1f 100644 --- a/chromium/chrome/browser/resources/settings/about_page/about_page.js +++ b/chromium/chrome/browser/resources/settings/about_page/about_page.js @@ -96,6 +96,12 @@ Polymer({ value: false, }, + /** @private */ + showTPMFirmwareUpdateLineItem_: Boolean, + + /** @private */ + showTPMFirmwareUpdateDialog_: Boolean, + /** @private {!AboutPageUpdateInfo|undefined} */ updateInfo_: Object, // </if> @@ -168,6 +174,12 @@ Polymer({ this.onPromoteUpdaterStatusChanged_.bind(this)); // </if> this.aboutBrowserProxy_.refreshUpdateStatus(); + // <if expr="chromeos"> + this.addWebUIListener( + 'tpm-firmware-update-status-changed', + this.onTPMFirmwareUpdateStatusChanged_.bind(this)); + this.aboutBrowserProxy_.refreshTPMFirmwareUpdateStatus(); + // </if> }, /** @@ -206,6 +218,7 @@ Polymer({ return; this.aboutBrowserProxy_.promoteUpdater(); }, + // </if> /** * @param {!Event} event @@ -216,7 +229,6 @@ Polymer({ // actionable items won't trigger action. event.stopPropagation(); }, - // </if> /** @private */ onHelpTap_: function() { @@ -443,6 +455,24 @@ Polymer({ // dialog and then intends to check for update again. this.hasCheckedForUpdates_ = false; }, + + /** + * @param {!TPMFirmwareUpdateStatusChangedEvent} event + * @private + */ + onTPMFirmwareUpdateStatusChanged_: function(event) { + this.showTPMFirmwareUpdateLineItem_ = event.updateAvailable; + }, + + /** @private */ + onTPMFirmwareUpdateTap_: function() { + this.showTPMFirmwareUpdateDialog_ = true; + }, + + /** @private */ + onPowerwashDialogClose_: function() { + this.showTPMFirmwareUpdateDialog_ = false; + }, // </if> /** @private */ diff --git a/chromium/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js b/chromium/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js index df85fd8c99e..ecd5043689c 100644 --- a/chromium/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js @@ -52,6 +52,13 @@ var BrowserChannel = { DEV: 'dev-channel', STABLE: 'stable-channel', }; + +/** + * @typedef {{ + * updateAvailable: boolean, + * }} + */ +var TPMFirmwareUpdateStatusChangedEvent; // </if> /** @@ -190,6 +197,11 @@ cr.define('settings', function() { /** @return {!Promise<?RegulatoryInfo>} */ getRegulatoryInfo() {} + /** + * Request TPM firmware update status from the browser. It results in one or + * more 'tpm-firmware-update-status-changed' WebUI events. + */ + refreshTPMFirmwareUpdateStatus() {} // </if> // <if expr="_google_chrome and is_macosx"> @@ -265,6 +277,11 @@ cr.define('settings', function() { getRegulatoryInfo() { return cr.sendWithPromise('getRegulatoryInfo'); } + + /** @override */ + refreshTPMFirmwareUpdateStatus() { + chrome.send('refreshTPMFirmwareUpdateStatus'); + } // </if> } 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 da28035f148..7ac5845998d 100644 --- a/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html +++ b/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html @@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/html/md_select_css.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html"> @@ -36,51 +37,46 @@ }; width: 100%; /* Pushes policy indicators to end. */ } + + /* The theme mixes a link and a paper-button divided by a separator with + * 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: 8px; + } + + #themeRow .separator { + -webkit-margin-start: 0; + } + + #useSystem { + -webkit-margin-start: calc(var(--cr-button-edge-spacing) * -1); + } </style> <settings-animated-pages id="pages" section="appearance" focus-config="[[focusConfig_]]"> <neon-animatable route-path="default"> <if expr="chromeos"> - <div class="settings-box first two-line" id="wallpaperButton" - on-tap="openWallpaperManager_" actionable - hidden="[[!pageVisibility.setWallpaper]]"> - <div class="start"> - $i18n{setWallpaper} - <div class="secondary" id="wallpaperSecondary"> - $i18n{openWallpaperApp} - </div> - </div> + <button icon-class="icon-external" id="wallpaperButton" + is="cr-link-row" + hidden="[[!pageVisibility.setWallpaper]]" + on-tap="openWallpaperManager_" + label="$i18n{setWallpaper}" sub-label="$i18n{openWallpaperApp}" + disabled="[[isWallpaperPolicyControlled_]]"> <template is="dom-if" if="[[isWallpaperPolicyControlled_]]"> <cr-policy-indicator id="wallpaperPolicyIndicator" indicator-type="devicePolicy"> </cr-policy-indicator> </template> - <button class="icon-external" id="showWallpaperManager" - is="paper-icon-button-light" aria-label="$i18n{setWallpaper}" - disabled="[[isWallpaperPolicyControlled_]]" - aria-describedby="wallpaperSecondary"> - </button> - </div> - <div class="settings-box two-line" - hidden="[[!pageVisibility.setTheme]]"> + </button> + <div class="hr"></div> </if> -<if expr="not chromeos"> - <div class="settings-box two-line first" + <div class="settings-row continuation" id="themeRow" hidden="[[!pageVisibility.setTheme]]"> -</if> - <a class="start two-line inherit-color no-outline" tabindex="-1" - target="_blank" href$="[[getThemeHref_(themeUrl_)]]"> - <div class="flex"> - $i18n{themes} - <div class="secondary" id="themesSecondary"> - [[themeSublabel_]] - </div> - </div> - <button class="icon-external" is="paper-icon-button-light" - actionable aria-label="$i18n{themes}" - aria-describedby="themesSecondary"> - </button> - </a> + <button class="first" icon-class="icon-external" is="cr-link-row" + hidden="[[!pageVisibility.setTheme]]" + label="$i18n{themes}" sub-label="[[themeSublabel_]]" + on-tap="openThemeUrl_"></button> <if expr="not is_linux or chromeos"> <template is="dom-if" if="[[prefs.extensions.theme.id.value]]"> <div class="separator"></div> @@ -91,7 +87,8 @@ </template> </if> <if expr="is_linux and not chromeos"> - <div class="settings-row" hidden="[[!showThemesSecondary_( + <div class="settings-row continuation" + hidden="[[!showThemesSecondary_( prefs.extensions.theme.id.value, useSystemTheme_)]]" id="themesSecondaryActions"> <div class="separator"></div> @@ -169,14 +166,10 @@ menu-options="[[fontSizeOptions_]]"> </settings-dropdown-menu> </div> - <div id="customize-fonts-subpage-trigger" class="settings-box" - on-tap="onCustomizeFontsTap_" actionable> - <div class="start"> - $i18n{customizeFonts} - </div> - <button class="subpage-arrow" is="paper-icon-button-light" - aria-label="$i18n{customizeFonts}"></button> - </div> + <button class="hr" is="cr-link-row" + icon-class="subpage-arrow" id="customize-fonts-subpage-trigger" + label="$i18n{customizeFonts}" on-tap="onCustomizeFontsTap_"> + </button> <div class="settings-box" hidden="[[!pageVisibility.pageZoom]]"> <div id="pageZoom" class="start">$i18n{pageZoom}</div> <div class="md-select-wrapper"> diff --git a/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.js b/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.js index be01b65fc12..e3c34ea3b66 100644 --- a/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.js +++ b/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.js @@ -104,8 +104,7 @@ Polymer({ var map = new Map(); if (settings.routes.FONTS) { map.set( - settings.routes.FONTS.path, - '#customize-fonts-subpage-trigger .subpage-arrow'); + settings.routes.FONTS.path, '#customize-fonts-subpage-trigger'); } return map; }, @@ -198,12 +197,11 @@ Polymer({ }, /** - * URL for either current theme or the theme gallery. - * @return {string} + * Open URL for either current theme or the theme gallery. * @private */ - getThemeHref_: function() { - return this.themeUrl_ || loadTimeData.getString('themesGalleryUrl'); + openThemeUrl_: function() { + window.open(this.themeUrl_ || loadTimeData.getString('themesGalleryUrl')); }, // <if expr="chromeos"> 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 f622ff71a9e..cb5157281cf 100644 --- a/chromium/chrome/browser/resources/settings/basic_page/basic_page.html +++ b/chromium/chrome/browser/resources/settings/basic_page/basic_page.html @@ -162,7 +162,16 @@ <template is="dom-if" if="[[showPage_(pageVisibility.search)]]" restamp> <settings-section page-title="$i18n{searchPageTitle}" section="search"> +<if expr="chromeos"> + <settings-search-page prefs="{{prefs}}" + arc-enabled="[[prefs.arc.enabled.value]]" + voice-interaction-value-prop-accepted="[[ + prefs.arc.voice_interaction_value_prop.accepted.value]]"> + </settings-search-page> +</if> +<if expr="not chromeos"> <settings-search-page prefs="{{prefs}}"></settings-search-page> +</if> </settings-section> </template> <if expr="chromeos"> 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 da7f5f65ebe..36b2b7f6423 100644 --- a/chromium/chrome/browser/resources/settings/basic_page/basic_page.js +++ b/chromium/chrome/browser/resources/settings/basic_page/basic_page.js @@ -39,10 +39,7 @@ Polymer({ showChangePassword: { type: Boolean, - value: function() { - return loadTimeData.valueExists('changePasswordEnabled') && - loadTimeData.getBoolean('changePasswordEnabled'); - }, + value: false, }, /** @@ -99,6 +96,10 @@ Polymer({ currentRoute_: Object, }, + hostAttributes: { + role: 'main', + }, + listeners: { 'subpage-expand': 'onSubpageExpanded_', }, @@ -107,16 +108,24 @@ Polymer({ attached: function() { this.currentRoute_ = settings.getCurrentRoute(); - this.addEventListener('chrome-cleanup-dismissed', e => { + // <if expr="is_win"> + this.addEventListener('chrome-cleanup-dismissed', () => { this.showChromeCleanup = false; }); + // </if> - this.addEventListener('change-password-clicked', e => { + this.addEventListener('change-password-dismissed', () => { this.showChangePassword = false; }); + this.addWebUIListener('change-password-visibility', visibility => { + this.showChangePassword = visibility; + }); + settings.ChangePasswordBrowserProxyImpl.getInstance() + .initializeChangePasswordHandler(); + if (settings.AndroidAppsBrowserProxyImpl) { - cr.addWebUIListener( + this.addWebUIListener( 'android-apps-info-update', this.androidAppsInfoUpdate_.bind(this)); settings.AndroidAppsBrowserProxyImpl.getInstance() .requestAndroidAppsInfo(); 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 2a6445caf7c..dd816a8810d 100644 --- a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js +++ b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js @@ -206,10 +206,10 @@ Polymer({ this.bluetoothToggleDisabled_ = true; this.bluetoothPrivate.setAdapterState( {powered: this.bluetoothToggleState_}, () => { - var error = chrome.runtime.lastError; - if (error && error != 'Error setting adapter properties: powered') { - console.error('Error enabling bluetooth: ' + error.message); - return; + if (chrome.runtime.lastError) { + console.error( + 'Error enabling bluetooth: ' + + chrome.runtime.lastError.message); } this.setPrefValue( 'ash.user.bluetooth.adapter_enabled', diff --git a/chromium/chrome/browser/resources/settings/change_password_page/change_password_browser_proxy.js b/chromium/chrome/browser/resources/settings/change_password_page/change_password_browser_proxy.js index d7b545532fb..8118cd8ccaa 100644 --- a/chromium/chrome/browser/resources/settings/change_password_page/change_password_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/change_password_page/change_password_browser_proxy.js @@ -5,11 +5,8 @@ cr.define('settings', function() { /** @interface */ class ChangePasswordBrowserProxy { - /** - * Inform PasswordProtectionService that the change password card is - * showing. - */ - onChangePasswordPageShown() {} + /** Initialize the change password handler.*/ + initializeChangePasswordHandler() {} /** * Initiate the change password process. e.g., for Gmail users, it @@ -24,8 +21,8 @@ cr.define('settings', function() { */ class ChangePasswordBrowserProxyImpl { /** @override */ - onChangePasswordPageShown() { - chrome.send('onChangePasswordPageShown'); + initializeChangePasswordHandler() { + chrome.send('initializeChangePasswordHandler'); } /** @override */ 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 8efd4112cde..deb22750820 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 @@ -7,6 +7,7 @@ <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.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-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-styles/color.html"> diff --git a/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.js b/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.js index a17ece0fecc..def50ad1000 100644 --- a/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.js +++ b/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.js @@ -10,30 +10,10 @@ Polymer({ is: 'settings-change-password-page', - properties: { - /** - * Preferences state. - */ - prefs: { - type: Object, - notify: true, - }, - }, - - /** @private {?settings.ChangePasswordBrowserProxy} */ - browserProxy_: null, - - /** @override */ - attached: function() { - this.browserProxy_ = settings.ChangePasswordBrowserProxyImpl.getInstance(); - this.browserProxy_.onChangePasswordPageShown(); - }, - /** @private */ changePassword_: function() { listenOnce(this, 'transitionend', () => { - this.browserProxy_.changePassword(); + settings.ChangePasswordBrowserProxyImpl.getInstance().changePassword(); }); - this.fire('change-password-clicked'); }, }); diff --git a/chromium/chrome/browser/resources/settings/change_password_page/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/change_password_page/compiled_resources2.gyp index fea6ac8f461..64476e15c62 100644 --- a/chromium/chrome/browser/resources/settings/change_password_page/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/change_password_page/compiled_resources2.gyp @@ -17,6 +17,7 @@ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', '<(EXTERNS_GYP):settings_private', 'change_password_browser_proxy', ], 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 cc7eee42195..d2c2fc0ae4f 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 @@ -129,11 +129,11 @@ <div class="start"> $i18n{chromeCleanupLinkShowFiles} </div> - <cr-expand-button expanded="{{showFilesToRemove_}}" + <cr-expand-button expanded="{{filesToRemoveListExpanded_}}" alt="$i18n{chromeCleanupLinkShowFiles}"> </cr-expand-button> </div> - <iron-collapse opened="[[showFilesToRemove_]]"> + <iron-collapse opened="[[filesToRemoveListExpanded_]]"> <div id="files-to-remove-container"> <ul id="files-to-remove-list" class="secondary"> <template is="dom-repeat" items="[[filesToRemove_]]" as="fileName"> 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 9ff10384a20..caaf5da8706 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 @@ -31,6 +31,57 @@ settings.ChromeCleanupDismissSource = { }; /** + * The possible states for the cleanup card. + * @enum {string} + */ +settings.ChromeCleanerCardState = { + HIDDEN_CARD: 'hidden_card', + CLEANUP_OFFERED: 'cleanup_offered', + CLEANING: 'cleaning', + REBOOT_REQUIRED: 'reboot_required', + CLEANUP_SUCCEEDED: 'cleanup_succeeded', + CLEANING_FAILED: 'cleanup_failed', +}; + +/** + * Boolean properties for a cleanup card state. + * @enum {number} + */ +settings.ChromeCleanupCardFlags = { + NONE: 0, + SHOW_DETAILS: 1 << 0, + SHOW_LOGS_PERMISSIONS: 1 << 1, + SHOW_LEARN_MORE: 1 << 2, + IS_REMOVING: 1 << 3, +}; + +/** + * @typedef {{ + * statusIcon: string, + * statusIconClassName: string, + * }} + */ +settings.ChromeCleanupCardIcon; + +/** + * @typedef {{ + * label: string, + * doAction: !function(), + * }} + */ +settings.ChromeCleanupCardActionButton; + +/** + * @typedef {{ + * title: ?string, + * icon: ?settings.ChromeCleanupCardIcon, + * actionButton: ?settings.ChromeCleanupCardActionButton, + * flags: number, + * }} + */ +settings.ChromeCleanupCardComponents; + +/** * @fileoverview * 'settings-chrome-cleanup-page' is the settings page containing Chrome * Cleanup settings. @@ -95,10 +146,10 @@ Polymer({ }, /** @private */ - showFilesToRemove_: { + filesToRemoveListExpanded_: { type: Boolean, value: false, - observer: 'showFilesToRemoveChanged_', + observer: 'filesToRemoveListExpandedChanged_', }, /** @private */ @@ -143,9 +194,14 @@ Polymer({ /** @private {?function()} */ doAction_: null, + /** @private {?Map<settings.ChromeCleanerCardState, + * !settings.ChromeCleanupCardComponents>} */ + cardStateToComponentsMap_: null, + /** @override */ attached: function() { this.browserProxy_ = settings.ChromeCleanupProxyImpl.getInstance(); + this.cardStateToComponentsMap_ = this.buildCardStateToComponentsMap_(); this.addWebUIListener('chrome-cleanup-on-idle', this.onIdle_.bind(this)); this.addWebUIListener( @@ -198,12 +254,11 @@ Polymer({ * Notify Chrome that the details section was opened or closed. * @private */ - showFilesToRemoveChanged_: function() { + filesToRemoveListExpandedChanged_: function() { if (this.browserProxy_) - this.browserProxy_.notifyShowDetails(this.showFilesToRemove_); + this.browserProxy_.notifyShowDetails(this.filesToRemoveListExpanded_); }, - /** * Notfies Chrome that the "learn more" link was clicked. * @private @@ -217,7 +272,7 @@ Polymer({ * @private */ showPoweredBy_: function() { - return this.showFilesToRemove_ && this.isPartnerPowered_; + return this.filesToRemoveListExpanded_ && this.isPartnerPowered_; }, /** @@ -227,76 +282,48 @@ Polymer({ */ onIdle_: function(idleReason) { if (idleReason == settings.ChromeCleanupIdleReason.CLEANING_SUCCEEDED) { - this.title_ = this.i18n('chromeCleanupTitleRemoved'); - this.enableActionButton_( - this.i18n('chromeCleanupDoneButtonLabel'), - this.dismiss_.bind( - this, - settings.ChromeCleanupDismissSource.CLEANUP_SUCCESS_DONE_BUTTON)); - this.setIconDone_(); - this.showLearnMore_ = false; + this.renderCleanupCard_( + settings.ChromeCleanerCardState.CLEANUP_SUCCEEDED, []); } else if (idleReason == settings.ChromeCleanupIdleReason.INITIAL) { this.dismiss_(settings.ChromeCleanupDismissSource.OTHER); } else { // Scanning-related idle reasons are unexpected. Show an error message for // all reasons other than |CLEANING_SUCCEEDED| and |INITIAL|. - this.title_ = this.i18n('chromeCleanupTitleErrorCantRemove'); - this.enableActionButton_( - this.i18n('chromeCleanupDoneButtonLabel'), - this.dismiss_.bind( - this, - settings.ChromeCleanupDismissSource.CLEANUP_FAILURE_DONE_BUTTON)); - this.setIconWarning_(); - this.showLearnMore_ = true; + this.renderCleanupCard_( + settings.ChromeCleanerCardState.CLEANING_FAILED, []); } - - this.isRemoving_ = false; - this.disableDetails_(); }, /** * Listener of event 'chrome-cleanup-on-scanning'. - * No UI will be shown in the Settings page on that state, so we simply hide - * the card and cleanup this element's fields. + * No UI will be shown in the Settings page on that state, simply hide the + * card and cleanup this element's fields. * @private */ onScanning_: function() { - this.title_ = ''; - this.isRemoving_ = false; - this.disableActionButton_(); - this.disableDetails_(); + this.renderCleanupCard_(settings.ChromeCleanerCardState.HIDDEN_CARD, []); }, /** * Listener of event 'chrome-cleanup-on-infected'. * Offers a cleanup to the user and enables presenting files to be removed. - * @param {!Array<!string>} files The list of files to present to the user. + * @param {!Array<string>} files The list of files to present to the user. * @private */ onInfected_: function(files) { - this.title_ = this.i18n('chromeCleanupTitleRemove'); - this.isRemoving_ = false; - this.setIconRemove_(); - this.enableActionButton_( - this.i18n('chromeCleanupRemoveButtonLabel'), - this.startCleanup_.bind(this)); - this.enableDetails_(files); + this.renderCleanupCard_( + settings.ChromeCleanerCardState.CLEANUP_OFFERED, files); }, /** * Listener of event 'chrome-cleanup-on-cleaning'. * Shows a spinner indicating that an on-going action and enables presenting * files to be removed. - * @param {!Array<!string>} files The list of files to present to the user. + * @param {!Array<string>} files The list of files to present to the user. * @private */ onCleaning_: function(files) { - this.title_ = this.i18n('chromeCleanupTitleRemoving'); - this.isRemoving_ = true; - this.resetIcon_(); - this.disableActionButton_(); - this.enableDetails_(files); - this.showLogsPermission_ = false; + this.renderCleanupCard_(settings.ChromeCleanerCardState.CLEANING, files); }, /** @@ -306,100 +333,123 @@ Polymer({ * @private */ onRebootRequired_: function() { - this.title_ = this.i18n('chromeCleanupTitleRestart'); - this.isRemoving_ = false; - this.showLearnMore_ = false; - this.setIconDone_(); - this.enableActionButton_( - this.i18n('chromeCleanupRestartButtonLabel'), - this.restartComputer_.bind(this)); - this.disableDetails_(); + this.renderCleanupCard_( + settings.ChromeCleanerCardState.REBOOT_REQUIRED, []); }, /** - * Listener of event 'chrome-cleanup-dismiss'. - * Hides the Cleanup card. + * Renders the cleanup card given the state and list of files. + * @param {!settings.ChromeCleanerCardState} state The card state to be + * rendered. + * @param {!Array<string>} files The list of files to present to the user. * @private */ - onDismiss_: function() { - this.fire('chrome-cleanup-dismissed'); + renderCleanupCard_: function(state, files) { + var components = this.cardStateToComponentsMap_.get(state); + assert(components); + + this.filesToRemove_ = files; + this.title_ = components.title || ''; + this.updateIcon_(components.icon); + this.updateActionButton_(components.actionButton); + this.updateCardFlags_(components.flags); }, /** - * @param {boolean} enabled Whether logs upload is enabled. + * 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 */ - onUploadPermissionChange_: function(enabled) { - this.logsUploadPref_ = { - key: '', - type: chrome.settingsPrivate.PrefType.BOOLEAN, - value: enabled, - }; + updateIcon_: function(icon) { + if (!icon) { + this.statusIcon_ = ''; + this.statusIconClassName_ = ''; + } else { + this.statusIcon_ = icon.statusIcon; + this.statusIconClassName_ = icon.statusIconClassName; + } }, /** - * @param {boolean} enabled Whether to enable logs upload. + * Updates the action button on the cleanup card as the action expected for + * the current state. + * @param {?settings.ChromeCleanupCardActionButton} actionButton + * The button to render, or null if no button should be shown. * @private */ - changeLogsPermission_: function(enabled) { - var enabled = this.$.chromeCleanupLogsUploadControl.checked; - this.browserProxy_.setLogsUploadPermission(enabled); + updateActionButton_: function(actionButton) { + if (!actionButton) { + this.showActionButton_ = false; + this.actionButtonLabel_ = ''; + this.doAction_ = null; + } else { + this.showActionButton_ = true; + this.actionButtonLabel_ = actionButton.label; + this.doAction_ = actionButton.doAction; + } }, /** - * Dismiss the card. - * @param {number} source + * Updates boolean flags corresponding to optional components to be rendered + * on the card. + * @param {number} flags Flags indicating optional components to be rendered. * @private */ - dismiss_: function(source) { - this.browserProxy_.dismissCleanupPage(source); + updateCardFlags_: function(flags) { + this.showDetails_ = + (flags & settings.ChromeCleanupCardFlags.SHOW_DETAILS) != 0; + this.showLogsPermission_ = + (flags & settings.ChromeCleanupCardFlags.SHOW_LOGS_PERMISSIONS) != 0; + this.showLearnMore_ = + (flags & settings.ChromeCleanupCardFlags.SHOW_LEARN_MORE) != 0; + this.isRemoving_ = + (flags & settings.ChromeCleanupCardFlags.IS_REMOVING) != 0; + + // Files to remove list should only be expandable if details are being + // shown, otherwise it will add extra padding at the bottom of the card. + if (!this.showDetails_) + this.filesToRemoveListExpanded_ = false; }, /** - * Hides the action button in the card when no action is available. + * Listener of event 'chrome-cleanup-dismiss'. + * Hides the Cleanup card. * @private */ - disableActionButton_: function() { - this.showActionButton_ = false; - this.actionButtonLabel_ = ''; - this.doAction_ = null; + onDismiss_: function() { + this.fire('chrome-cleanup-dismissed'); }, /** - * Shows the action button in the card with an associated label and action - * function. - * @param {!string} buttonLabel The label for the action button. - * @param {!function()} action The function associated with the on-tap event. + * @param {boolean} enabled Whether logs upload is enabled. * @private */ - enableActionButton_: function(buttonLabel, action) { - this.showActionButton_ = true; - this.actionButtonLabel_ = buttonLabel; - this.doAction_ = action; + onUploadPermissionChange_: function(enabled) { + this.logsUploadPref_ = { + key: '', + type: chrome.settingsPrivate.PrefType.BOOLEAN, + value: enabled, + }; }, /** - * Disables the details section on the card. + * @param {boolean} enabled Whether to enable logs upload. * @private */ - disableDetails_: function() { - this.showDetails_ = false; - this.showLogsPermission_ = false; - this.showFilesToRemove_ = false; - this.filesToRemove_ = []; + changeLogsPermission_: function(enabled) { + var enabled = this.$.chromeCleanupLogsUploadControl.checked; + this.browserProxy_.setLogsUploadPermission(enabled); }, /** - * Enables the details section on the card. - * @param {!Array<!string>} files The list of files to present to the user. + * Dismiss the card. + * @param {settings.ChromeCleanupDismissSource} source * @private */ - enableDetails_: function(files) { - this.showDetails_ = true; - this.showLogsPermission_ = true; - this.showLearnMore_ = true; - // Note: doesn't change the state of this.showFilesToRemove_. - this.filesToRemove_ = files; + dismiss_: function(source) { + this.renderCleanupCard_(settings.ChromeCleanerCardState.HIDDEN_CARD, []); + this.browserProxy_.dismissCleanupPage(source); }, /** @@ -420,38 +470,120 @@ Polymer({ }, /** - * Sets the card's icon as the cleanup offered indication. + * Returns the map of card states to components to be rendered. + * @return {!Map<settings.ChromeCleanerCardState, + * !settings.ChromeCleanupCardComponents>} * @private */ - setIconRemove_: function() { - this.statusIcon_ = 'settings:security'; - this.statusIconClassName_ = 'status-icon-remove'; - }, + buildCardStateToComponentsMap_: function() { + /** + * The icons to show on the card. + * @enum {settings.ChromeCleanupCardIcon} + */ + var icons = { + // Card's icon indicates a cleanup offer. + REMOVE: { + statusIcon: 'settings:security', + statusIconClassName: 'status-icon-remove', + }, - /** - * Sets the card's icon as a warning (in case of failure). - * @private - */ - setIconWarning_: function() { - this.statusIcon_ = 'settings:error'; - this.statusIconClassName_ = 'status-icon-warning'; - }, + // Card's icon indicates a warning (in case of failure). + WARNING: { + statusIcon: 'settings:error', + statusIconClassName: 'status-icon-warning', + }, - /** - * Sets the card's icon as a completed or reboot required indication. - * @private - */ - setIconDone_: function() { - this.statusIcon_ = 'settings:check-circle'; - this.statusIconClassName_ = 'status-icon-done'; - }, + // Card's icon indicates completion or reboot required. + DONE: { + statusIcon: 'settings:check-circle', + statusIconClassName: 'status-icon-done', + }, + }; - /** - * Resets the card's icon. - * @private - */ - resetIcon_: function() { - this.statusIcon_ = ''; - this.statusIconClassName_ = ''; + /** + * The action buttons to show on the card. + * @enum {settings.ChromeCleanupCardActionButton} + */ + var actionButtons = { + REMOVE: { + label: this.i18n('chromeCleanupRemoveButtonLabel'), + doAction: this.startCleanup_.bind(this), + }, + + RESTART_COMPUTER: { + label: this.i18n('chromeCleanupRestartButtonLabel'), + doAction: this.restartComputer_.bind(this), + }, + + DISMISS_CLEANUP_SUCCESS: { + label: this.i18n('chromeCleanupDoneButtonLabel'), + doAction: this.dismiss_.bind( + this, + settings.ChromeCleanupDismissSource.CLEANUP_SUCCESS_DONE_BUTTON), + }, + + DISMISS_CLEANUP_FAILURE: { + label: this.i18n('chromeCleanupDoneButtonLabel'), + doAction: this.dismiss_.bind( + this, + settings.ChromeCleanupDismissSource.CLEANUP_FAILURE_DONE_BUTTON), + }, + }; + + return new Map([ + [ + settings.ChromeCleanerCardState.HIDDEN_CARD, { + title: null, + icon: null, + actionButton: null, + flags: settings.ChromeCleanupCardFlags.NONE, + } + ], + [ + settings.ChromeCleanerCardState.CLEANUP_OFFERED, { + title: this.i18n('chromeCleanupTitleRemove'), + icon: icons.REMOVE, + actionButton: actionButtons.REMOVE, + flags: settings.ChromeCleanupCardFlags.SHOW_DETAILS | + settings.ChromeCleanupCardFlags.SHOW_LOGS_PERMISSIONS | + settings.ChromeCleanupCardFlags.SHOW_LEARN_MORE, + } + ], + [ + settings.ChromeCleanerCardState.CLEANING, { + title: this.i18n('chromeCleanupTitleRemoving'), + icon: null, + actionButton: null, + flags: settings.ChromeCleanupCardFlags.SHOW_DETAILS | + settings.ChromeCleanupCardFlags.IS_REMOVING | + settings.ChromeCleanupCardFlags.SHOW_LEARN_MORE, + } + ], + [ + settings.ChromeCleanerCardState.REBOOT_REQUIRED, { + title: this.i18n('chromeCleanupTitleRestart'), + icon: icons.DONE, + actionButton: actionButtons.RESTART_COMPUTER, + flags: settings.ChromeCleanupCardFlags.NONE, + } + ], + [ + settings.ChromeCleanerCardState.CLEANUP_SUCCEEDED, { + title: this.i18n('chromeCleanupTitleRemoved'), + icon: icons.DONE, + actionButton: actionButtons.DISMISS_CLEANUP_SUCCESS, + flags: settings.ChromeCleanupCardFlags.NONE, + } + ], + [ + settings.ChromeCleanerCardState.CLEANING_FAILED, { + title: this.i18n('chromeCleanupTitleErrorCantRemove'), + icon: icons.WARNING, + actionButton: actionButtons.DISMISS_CLEANUP_FAILURE, + flags: settings.ChromeCleanupCardFlags.SHOW_LEARN_MORE | + settings.ChromeCleanupCardFlags.NONE, + } + ], + ]); }, }); 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 c78b9dd84d7..6bf142aac63 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 @@ -99,7 +99,8 @@ Polymer({ /** @override */ ready: function() { this.$.clearFrom.menuOptions = this.clearFromOptions_; - this.addWebUIListener('update-footer', this.updateFooter_.bind(this)); + this.addWebUIListener( + 'update-sync-state', this.updateSyncState_.bind(this)); this.addWebUIListener( 'update-counter-text', this.updateCounterText_.bind(this)); }, @@ -122,12 +123,13 @@ Polymer({ /** * Updates the footer to show only those sentences that are relevant to this * user. + * @param {boolean} signedIn Whether the user is signed in. * @param {boolean} syncing Whether the user is syncing data. * @param {boolean} otherFormsOfBrowsingHistory Whether the user has other * forms of browsing history in their account. * @private */ - updateFooter_: function(syncing, otherFormsOfBrowsingHistory) { + updateSyncState_: function(signedIn, syncing, otherFormsOfBrowsingHistory) { this.$.googleFooter.hidden = !otherFormsOfBrowsingHistory; this.$.syncedDataSentence.hidden = !syncing; this.$.clearBrowsingDataDialog.classList.add('fully-rendered'); diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.html b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.html index b991c9986fe..502eac00064 100644 --- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.html +++ b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.html @@ -2,8 +2,10 @@ <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.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"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-tabs/paper-tabs.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="clear_browsing_data_browser_proxy.html"> <link rel="import" href="history_deletion_dialog.html"> @@ -12,12 +14,31 @@ <link rel="import" href="../controls/settings_dropdown_menu.html"> <link rel="import" href="../icons.html"> <link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="../settings_vars_css.html"> <!-- This file is a fork of clear_browsing_data_dialog.html until the new CBD UI is launched. --> <dom-module id="settings-clear-browsing-data-dialog-tabs"> <template> <style include="settings-shared"> + :host { + /* Fixed height to allow multiple tabs with different height. + * The last entry in the advanced tab should show half an entry. + * crbug.com/652027 */ + --body-container-height: 322px; + } + + #clearBrowsingDataDialog { + --cr-dialog-top-container-min-height: 42px; + --cr-dialog-title: { + padding-bottom: 8px; + }; + --cr-dialog-body-container: { + border-top: 1px solid var(--paper-grey-300); + height: var(--body-container-height); + }; + } + #clearBrowsingDataDialog:not(.fully-rendered) { visibility: hidden; } @@ -26,6 +47,16 @@ color: var(--paper-grey-600); } + #clearBrowsingDataDialog [slot=body] { + padding-top: 8px; + } + + #importantSitesDialog { + --cr-dialog-body-container: { + height: var(--body-container-height); + }; + } + .row { align-items: center; display: flex; @@ -43,55 +74,36 @@ --settings-row-two-line-min-height: 48px; --settings-checkbox-label: { line-height: 1.25rem; - }; - } - - #generalFooter { - margin: 0; - min-height: 18px; - } - - #generalFooter iron-icon { - height: 18px; - padding: 1px; - width: 18px; - } - - #googleFooter { - margin: 0 0 0.8em 0; - min-height: 16px; - } - - #googleFooter iron-icon { - height: 16px; - padding: 2px; - width: 16px; - } - - [slot=footer] iron-icon { - margin: auto; + } } - .clear-browsing-data-footer { - -webkit-padding-start: 4px; - align-items: flex-start; - display: flex; - line-height: 1.538em; /* 20px/13px */ + #basic-tab settings-checkbox + settings-checkbox { + --settings-checkbox-margin-top: 12px; } - .clear-browsing-data-footer .footer-text { - -webkit-margin-start: 16px; + paper-tabs { + --paper-tabs-selection-bar-color: var(--google-blue-500); + --paper-tabs: { + font-size: 100%; + height: 40px; + } } - .clear-browsing-data-footer iron-icon { - flex-shrink: 0; + paper-tab { + --paper-tab-content: { + color: var(--google-blue-700); + }; + --paper-tab-content-unselected: { + opacity: 1; + color: var(--paper-grey-600); + }; } - .clear-browsing-data-footer a { - text-decoration: none; + .time-range-row { + margin-bottom: 12px; } - #clearFrom { + .time-range-select { -webkit-margin-start: 0.5em; /* Adjust for md-select-underline and 1px additional bottom padding * to keep md-select's text (without the underline) aligned with @@ -103,87 +115,141 @@ font-size: calc(13 / 15 * 100%); padding-top: 8px; } - - /* Cap the height on smaller screens to avoid unfavorable clipping. - * Replace the bottom margin with padding to avoid the gap between - * the scrollbar and the bottom separator. */ - @media all and (max-height: 724px) { - #clearBrowsingDataDialog { - /* crbug.com/652027: Show four and a *half* items in the list. */ - --cr-dialog-body-container: { - max-height: 280px; - }; - } - } </style> <dialog is="cr-dialog" id="clearBrowsingDataDialog" on-close="onClearBrowsingDataDialogClose_" - close-text="$i18n{close}" ignore-popstate> - <div slot="title">$i18n{clearBrowsingData} - NEW UI</div> + close-text="$i18n{close}" ignore-popstate has-tabs> + <div slot="title"> + <div>$i18n{clearBrowsingData}</div> + </div> + <div slot="header"> + <paper-tabs noink on-selected-changed="recordTabChange_" + selected="{{prefs.browser.last_clear_browsing_data_tab.value}}"> + <paper-tab>$i18n{basicPageTitle}</paper-tab> + <paper-tab>$i18n{advancedPageTitle}</paper-tab> + </paper-tabs> + </div> <div slot="body"> - <div class="row"> - $i18n{clearFollowingItemsFrom} - <settings-dropdown-menu id="clearFrom" - label="$i18n{clearFollowingItemsFrom}" - pref="{{prefs.browser.clear_data.time_period}}" - menu-options="[[clearFromOptions_]]"> - </settings-dropdown-menu> - </div> - <!-- Note: whether these checkboxes are checked are ignored if deleting - history is disabled (i.e. supervised users, policy), so it's OK to - have a hidden checkbox that's also checked (as the C++ accounts for - whether a user is allowed to delete history independently). --> - <settings-checkbox id="browsingCheckbox" class="browsing-data-checkbox" - pref="{{prefs.browser.clear_data.browsing_history}}" - label="$i18n{clearBrowsingHistory}" - sub-label="[[counters_.browsing_history]]" - disabled="[[clearingInProgress_]]" - hidden="[[isSupervised_]]"> - </settings-checkbox> - <settings-checkbox id="downloadCheckbox" class="browsing-data-checkbox" - pref="{{prefs.browser.clear_data.download_history}}" - label="$i18n{clearDownloadHistory}" - sub-label="[[counters_.download_history]]" - disabled="[[clearingInProgress_]]" - hidden="[[isSupervised_]]"> - </settings-checkbox> - <settings-checkbox id="cacheCheckbox" class="browsing-data-checkbox" - pref="{{prefs.browser.clear_data.cache}}" - label="$i18n{clearCache}" - sub-label="[[counters_.cache]]" - disabled="[[clearingInProgress_]]"> - </settings-checkbox> - <settings-checkbox id="cookiesCheckbox" class="browsing-data-checkbox" - pref="{{prefs.browser.clear_data.cookies}}" - label="$i18n{clearCookies}" - sub-label="$i18n{clearCookiesCounter}" - disabled="[[clearingInProgress_]]"> - </settings-checkbox> - <settings-checkbox class="browsing-data-checkbox" - pref="{{prefs.browser.clear_data.passwords}}" - label="$i18n{clearPasswords}" - sub-label="[[counters_.passwords]]" - disabled="[[clearingInProgress_]]"> - </settings-checkbox> - <settings-checkbox class="browsing-data-checkbox" - pref="{{prefs.browser.clear_data.form_data}}" - label="$i18n{clearFormData}" - sub-label="[[counters_.form_data]]" - disabled="[[clearingInProgress_]]"> - </settings-checkbox> - <settings-checkbox class="browsing-data-checkbox" - pref="{{prefs.browser.clear_data.hosted_apps_data}}" - label="$i18n{clearHostedAppData}" - sub-label="[[counters_.hosted_apps_data]]" - disabled="[[clearingInProgress_]]"> - </settings-checkbox> - <settings-checkbox class="browsing-data-checkbox" - pref="{{prefs.browser.clear_data.media_licenses}}" - label="$i18n{clearMediaLicenses}" - sub-label="[[counters_.media_licenses]]" - disabled="[[clearingInProgress_]]"> - </settings-checkbox> + <iron-pages id="tabs" + selected="[[prefs.browser.last_clear_browsing_data_tab.value]]"> + <div id="basic-tab"> + <div class="row time-range-row"> + <span class="time-range-label"> + $i18n{clearTimeRange} + </span> + <settings-dropdown-menu id="clearFromBasic" + class="time-range-select" + label="$i18n{clearTimeRange}" + pref="{{prefs.browser.clear_data.time_period_basic}}" + menu-options="[[clearFromOptions_]]"> + </settings-dropdown-menu> + </div> + <!-- Note: whether these checkboxes are checked are ignored if + deleting history is disabled (i.e. supervised users, policy), + so it's OK to have a hidden checkbox that's also checked (as + the C++ accounts for whether a user is allowed to delete + history independently). --> + <settings-checkbox id="browsingCheckboxBasic" + pref="{{prefs.browser.clear_data.browsing_history_basic}}" + label="$i18n{clearBrowsingHistory}" + sub-label-html="[[browsingCheckboxLabel_( + isSignedIn_, isSyncingHistory_, + '$i18nPolymer{clearBrowsingHistorySummary}', + '$i18nPolymer{clearBrowsingHistorySummarySignedIn}', + '$i18nPolymer{clearBrowsingHistorySummarySynced}')]]" + disabled="[[clearingInProgress_]]" + hidden="[[isSupervised_]]"> + </settings-checkbox> + <settings-checkbox id="cookiesCheckboxBasic" + class="cookies-checkbox" + pref="{{prefs.browser.clear_data.cookies_basic}}" + label="$i18n{clearCookies}" + sub-label="$i18n{clearCookiesSummary}" + disabled="[[clearingInProgress_]]"> + </settings-checkbox> + <settings-checkbox id="cacheCheckboxBasic" + class="cache-checkbox" + pref="{{prefs.browser.clear_data.cache_basic}}" + label="$i18n{clearCache}" + sub-label="[[counters_.cache_basic]]" + disabled="[[clearingInProgress_]]"> + </settings-checkbox> + </div> + <div id="advanced-tab"> + <div class="row time-range-row"> + <span class="time-range-label"> + $i18n{clearTimeRange} + </span> + <settings-dropdown-menu id="clearFrom" + class="time-range-select" + label="$i18n{clearTimeRange}" + pref="{{prefs.browser.clear_data.time_period}}" + menu-options="[[clearFromOptions_]]"> + </settings-dropdown-menu> + </div> + <settings-checkbox id="browsingCheckbox" + pref="{{prefs.browser.clear_data.browsing_history}}" + label="$i18n{clearBrowsingHistory}" + sub-label="[[counters_.browsing_history]]" + disabled="[[clearingInProgress_]]" + hidden="[[isSupervised_]]"> + </settings-checkbox> + <settings-checkbox id="downloadCheckbox" + pref="{{prefs.browser.clear_data.download_history}}" + label="$i18n{clearDownloadHistory}" + sub-label="[[counters_.download_history]]" + disabled="[[clearingInProgress_]]" + hidden="[[isSupervised_]]"> + </settings-checkbox> + <settings-checkbox id="cookiesCheckbox" + class="cookies-checkbox" + pref="{{prefs.browser.clear_data.cookies}}" + label="$i18n{clearCookies}" + sub-label="[[counters_.cookies]]" + disabled="[[clearingInProgress_]]"> + </settings-checkbox> + <settings-checkbox id="cacheCheckbox" + class="cache-checkbox" + pref="{{prefs.browser.clear_data.cache}}" + label="$i18n{clearCache}" + sub-label="[[counters_.cache]]" + disabled="[[clearingInProgress_]]"> + </settings-checkbox> + <settings-checkbox + pref="{{prefs.browser.clear_data.passwords}}" + label="$i18n{clearPasswords}" + sub-label="[[counters_.passwords]]" + disabled="[[clearingInProgress_]]"> + </settings-checkbox> + <settings-checkbox + pref="{{prefs.browser.clear_data.form_data}}" + label="$i18n{clearFormData}" + sub-label="[[counters_.form_data]]" + disabled="[[clearingInProgress_]]"> + </settings-checkbox> + <settings-checkbox + pref="{{prefs.browser.clear_data.site_settings}}" + label="[[siteSettingsLabel_( + '$i18nPolymer{siteSettings}', + '$i18nPolymer{contentSettings}')]]" + sub-label="[[counters_.site_settings]]" + disabled="[[clearingInProgress_]]"> + </settings-checkbox> + <settings-checkbox + pref="{{prefs.browser.clear_data.hosted_apps_data}}" + label="$i18n{clearHostedAppData}" + sub-label="[[counters_.hosted_apps_data]]" + disabled="[[clearingInProgress_]]"> + </settings-checkbox> + <settings-checkbox + pref="{{prefs.browser.clear_data.media_licenses}}" + label="$i18n{clearMediaLicenses}" + sub-label="[[counters_.media_licenses]]" + disabled="[[clearingInProgress_]]"> + </settings-checkbox> + </div> + </iron-pages> </div> <div slot="button-container"> <paper-spinner active="[[clearingInProgress_]]"></paper-spinner> @@ -192,25 +258,9 @@ <paper-button id="clearBrowsingDataConfirm" class="action-button" disabled="[[clearingInProgress_]]" on-tap="onClearBrowsingDataTap_"> - $i18n{clearBrowsingData} + $i18n{clearData} </paper-button> </div> - <div slot="footer"> - <div id="googleFooter" class="clear-browsing-data-footer"> - <iron-icon icon="settings:googleg"></iron-icon> - <div class="footer-text">$i18nRaw{otherFormsOfBrowsingHistory}</div> - </div> - <div id="generalFooter" class="clear-browsing-data-footer"> - <iron-icon icon="settings:info"></iron-icon> - <div class="footer-text"> - <span id="syncedDataSentence">$i18n{clearsSyncedData}</span> - <span>$i18n{warnAboutNonClearedData}</span> - <a id="clear-browser-data-old-learn-more-link" - href="$i18n{clearBrowsingDataLearnMoreUrl}" - target="_blank">$i18n{learnMore}</a> - </div> - </div> - </div> </dialog> <template is="dom-if" if="[[showImportantSitesDialog_]]"> @@ -219,13 +269,12 @@ <div slot="title"> $i18n{clearBrowsingData} <div class="secondary"> - <template is="dom-if" - if="[[!prefs.browser.clear_data.cache.value]]"> + <span hidden$="[[showImportantSitesCacheSubtitle_]]"> $i18n{importantSitesSubtitleCookies} - </template> - <template is="dom-if" if="[[prefs.browser.clear_data.cache.value]]"> + </span> + <span hidden$="[[!showImportantSitesCacheSubtitle_]]"> $i18n{importantSitesSubtitleCookiesAndCache} - </template> + </span> </div> </div> <div slot="body"> diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.js b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.js index b0c9479add2..b3f5ea698f8 100644 --- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.js +++ b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.js @@ -12,7 +12,7 @@ Polymer({ is: 'settings-clear-browsing-data-dialog-tabs', - behaviors: [WebUIListenerBehavior], + behaviors: [WebUIListenerBehavior, settings.RouteObserverBehavior], properties: { /** @@ -45,11 +45,11 @@ Polymer({ readOnly: true, type: Array, value: [ - {value: 0, name: loadTimeData.getString('clearDataHour')}, - {value: 1, name: loadTimeData.getString('clearDataDay')}, - {value: 2, name: loadTimeData.getString('clearDataWeek')}, - {value: 3, name: loadTimeData.getString('clearData4Weeks')}, - {value: 4, name: loadTimeData.getString('clearDataEverything')}, + {value: 0, name: loadTimeData.getString('clearPeriodHour')}, + {value: 1, name: loadTimeData.getString('clearPeriod24Hours')}, + {value: 2, name: loadTimeData.getString('clearPeriod7Days')}, + {value: 3, name: loadTimeData.getString('clearPeriod4Weeks')}, + {value: 4, name: loadTimeData.getString('clearPeriodEverything')}, ], }, @@ -73,6 +73,18 @@ Polymer({ value: false, }, + /** @private */ + isSignedIn_: { + type: Boolean, + value: false, + }, + + /** @private */ + isSyncingHistory_: { + type: Boolean, + value: false, + }, + /** @private {!Array<ImportantSite>} */ importantSites_: { type: Array, @@ -90,7 +102,25 @@ Polymer({ }, /** @private */ - showImportantSitesDialog_: {type: Boolean, value: false}, + showImportantSitesDialog_: { + type: Boolean, + value: false, + }, + + /** @private */ + showImportantSitesCacheSubtitle_: { + type: Boolean, + value: false, + }, + + /** + * Time in ms, when the dialog was opened. + * @private + */ + dialogOpenedTime_: { + type: Number, + value: 0, + } }, /** @private {settings.ClearBrowsingDataBrowserProxy} */ @@ -98,8 +128,8 @@ Polymer({ /** @override */ ready: function() { - this.$.clearFrom.menuOptions = this.clearFromOptions_; - this.addWebUIListener('update-footer', this.updateFooter_.bind(this)); + this.addWebUIListener( + 'update-sync-state', this.updateSyncState_.bind(this)); this.addWebUIListener( 'update-counter-text', this.updateCounterText_.bind(this)); }, @@ -108,6 +138,7 @@ Polymer({ attached: function() { this.browserProxy_ = settings.ClearBrowsingDataBrowserProxyImpl.getInstance(); + this.dialogOpenedTime_ = Date.now(); this.browserProxy_.initialize().then(() => { this.$.clearBrowsingDataDialog.showModal(); }); @@ -120,20 +151,69 @@ Polymer({ }, /** - * Updates the footer to show only those sentences that are relevant to this - * user. - * @param {boolean} syncing Whether the user is syncing data. + * Record visits to the CBD dialog. + * + * settings.RouteObserverBehavior + * @param {!settings.Route} currentRoute + * @protected + */ + currentRouteChanged: function(currentRoute) { + if (currentRoute == settings.routes.CLEAR_BROWSER_DATA) { + chrome.metricsPrivate.recordUserAction('ClearBrowsingData_DialogCreated'); + this.dialogOpenedTime_ = Date.now(); + } + }, + + /** + * Updates the history description to show the relevant information + * depending on sync and signin state. + * + * @param {boolean} signedIn Whether the user is signed in. + * @param {boolean} syncing Whether the user is syncing history. * @param {boolean} otherFormsOfBrowsingHistory Whether the user has other * forms of browsing history in their account. * @private */ - updateFooter_: function(syncing, otherFormsOfBrowsingHistory) { - this.$.googleFooter.hidden = !otherFormsOfBrowsingHistory; - this.$.syncedDataSentence.hidden = !syncing; + updateSyncState_: function(signedIn, syncing, otherFormsOfBrowsingHistory) { + this.isSignedIn_ = signedIn; + this.isSyncingHistory_ = syncing; this.$.clearBrowsingDataDialog.classList.add('fully-rendered'); }, /** + * Choose a summary checkbox label. + * @param {boolean} isSignedIn + * @param {boolean} isSyncingHistory + * @param {string} historySummary + * @param {string} historySummarySigned + * @param {string} historySummarySynced + * @return {string} + * @private + */ + browsingCheckboxLabel_: function( + isSignedIn, isSyncingHistory, historySummary, historySummarySigned, + historySummarySynced) { + if (isSyncingHistory) { + return historySummarySynced; + } else if (isSignedIn) { + return historySummarySigned; + } + return historySummary; + }, + + /** + * Choose a content/site settings label. + * @param {string} siteSettings + * @param {string} contentSettings + * @return {string} + * @private + */ + siteSettingsLabel_: function(siteSettings, contentSettings) { + return loadTimeData.getBoolean('enableSiteSettings') ? siteSettings : + contentSettings; + }, + + /** * Updates the text of a browsing data counter corresponding to the given * preference. * @param {string} prefName Browsing data type deletion preference. @@ -154,8 +234,10 @@ Polymer({ shouldShowImportantSites_: function() { if (!this.importantSitesFlagEnabled_) return false; - if (!this.$.cookiesCheckbox.checked) + var tab = this.$.tabs.selectedItem; + if (!tab.querySelector('.cookies-checkbox').checked) { return false; + } var haveImportantSites = this.importantSites_.length > 0; chrome.send( @@ -170,12 +252,13 @@ Polymer({ */ onClearBrowsingDataTap_: function() { if (this.shouldShowImportantSites_()) { + var tab = this.$.tabs.selectedItem; this.showImportantSitesDialog_ = true; + this.showImportantSitesCacheSubtitle_ = + tab.querySelector('.cache-checkbox').checked; this.$.clearBrowsingDataDialog.close(); // Show important sites dialog after dom-if is applied. - this.async(function() { - this.$$('#importantSitesDialog').showModal(); - }); + this.async(() => this.$$('#importantSitesDialog').showModal()); } else { this.clearBrowsingData_(); } @@ -198,21 +281,31 @@ Polymer({ */ clearBrowsingData_: function() { this.clearingInProgress_ = true; + var tab = this.$.tabs.selectedItem; - var checkboxes = this.root.querySelectorAll('.browsing-data-checkbox'); + checkboxes = tab.querySelectorAll('settings-checkbox'); var dataTypes = []; checkboxes.forEach((checkbox) => { if (checkbox.checked) dataTypes.push(checkbox.pref.key); }); - var timePeriod = this.$.clearFrom.pref.value; + var timePeriod = tab.querySelector('.time-range-select').pref.value; + + if (tab.id == 'basic-tab') { + chrome.metricsPrivate.recordUserAction('ClearBrowsingData_BasicTab'); + } else { + chrome.metricsPrivate.recordUserAction('ClearBrowsingData_AdvancedTab'); + } this.browserProxy_ .clearBrowsingData(dataTypes, timePeriod, this.importantSites_) .then(shouldShowNotice => { this.clearingInProgress_ = false; this.showHistoryDeletionDialog_ = shouldShowNotice; + chrome.metricsPrivate.recordMediumTime( + 'History.ClearBrowsingData.TimeSpentInDialog', + Date.now() - this.dialogOpenedTime_); if (!shouldShowNotice) this.closeDialogs_(); }); @@ -255,4 +348,20 @@ Polymer({ this.showHistoryDeletionDialog_ = false; this.closeDialogs_(); }, + + /** + * Records an action when the user changes between the basic and advanced tab. + * @param {!Event} event + * @private + */ + recordTabChange_: function(event) { + if (event.detail.value == 0) { + chrome.metricsPrivate.recordUserAction( + 'ClearBrowsingData_SwitchTo_BasicTab'); + } else { + chrome.metricsPrivate.recordUserAction( + 'ClearBrowsingData_SwitchTo_AdvancedTab'); + } + }, + }); diff --git a/chromium/chrome/browser/resources/settings/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/compiled_resources2.gyp index bec907b35a0..a0f14ce056a 100644 --- a/chromium/chrome/browser/resources/settings/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/compiled_resources2.gyp @@ -4,14 +4,6 @@ { 'targets': [ { - 'target_name': 'direction_delegate', - 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { 'target_name': 'extension_control_browser_proxy', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', diff --git a/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html b/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html index 4dca0444b47..b46ee716595 100644 --- a/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html +++ b/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html @@ -25,7 +25,7 @@ display: flex; /* Additional margin in case subLabel needs more than one line. */ margin-bottom: 4px; - margin-top: 4px; + margin-top: var(--settings-checkbox-margin-top, 4px); width: 100%; } 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 7776cccd157..3e6a81d6d48 100644 --- a/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html +++ b/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html @@ -53,7 +53,7 @@ } </style> <div id="outerRow" noSubLabel$="[[!subLabel]]"> - <div class="flex" id="labelWrapper" hidden="[[!label]]"> + <div class="flex" id="labelWrapper" hidden$="[[!label]]"> <div id="label" class="label">[[label]]</div> <div id="subLabel" class="secondary label">[[subLabel]]</div> </div> @@ -63,7 +63,8 @@ </cr-policy-pref-indicator> </template> <paper-toggle-button id="control" checked="{{checked}}" - on-change="notifyChangedByUserInteraction" aria-labelledby="label" + on-change="notifyChangedByUserInteraction" + aria-label$="[[getAriaLabel_(label, ariaLabel)]]" aria-describedby="subLabel" on-up="resetTrackLock_" disabled="[[controlDisabled_(disabled, pref)]]" on-tap="onToggleTap_"> diff --git a/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.js b/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.js index 1ac827c9fd8..96c789995a9 100644 --- a/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.js +++ b/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.js @@ -12,6 +12,13 @@ Polymer({ behaviors: [SettingsBooleanControlBehavior], properties: { + ariaLabel: { + type: String, + reflectToAttribute: false, // Handled by #control. + observer: 'onAriaLabelSet_', + value: '', + }, + elideLabel: { type: Boolean, reflectToAttribute: true, @@ -32,6 +39,26 @@ Polymer({ }, /** + * Removes the aria-label attribute if it's added by $i18n{...}. + * @private + */ + onAriaLabelSet_: function() { + if (this.hasAttribute('aria-label')) { + let ariaLabel = this.ariaLabel; + this.removeAttribute('aria-label'); + this.ariaLabel = ariaLabel; + } + }, + + /** + * @return {string} + * @private + */ + getAriaLabel_: function() { + return this.label || this.ariaLabel; + }, + + /** * Handle taps directly on the toggle (see: onLabelWrapperTap_ for non-toggle * taps). * @param {!Event} e diff --git a/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.html b/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.html index 40a923df826..0b573e387a1 100644 --- a/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.html +++ b/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.html @@ -55,7 +55,7 @@ <settings-toggle-button class="first" pref="{{prefs.settings.resolve_timezone_by_geolocation}}" id="timeZoneAutoDetect" - aria-labelledby="timezoneGeolocateToggleLabel"> + aria-label="$i18n{timeZoneGeolocation}"> </settings-toggle-button> </template> </div> diff --git a/chromium/chrome/browser/resources/settings/device_page/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/device_page/compiled_resources2.gyp index 14450adbb38..1f57d004daf 100644 --- a/chromium/chrome/browser/resources/settings/device_page/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/device_page/compiled_resources2.gyp @@ -110,6 +110,7 @@ 'target_name': 'night_light_slider', 'dependencies': [ '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior/compiled_resources2.gyp:iron-a11y-keys-behavior-extracted', + '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/compiled_resources2.gyp:iron-resizable-behavior-extracted', '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-behaviors/compiled_resources2.gyp:paper-inky-focus-behavior-extracted', '../prefs/compiled_resources2.gyp:prefs_behavior', ], diff --git a/chromium/chrome/browser/resources/settings/device_page/display.html b/chromium/chrome/browser/resources/settings/device_page/display.html index 6d2b3abf188..73d02acd988 100644 --- a/chromium/chrome/browser/resources/settings/device_page/display.html +++ b/chromium/chrome/browser/resources/settings/device_page/display.html @@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.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"> @@ -21,46 +22,43 @@ <dom-module id="settings-display"> <template> <style include="settings-shared md-select iron-flex iron-flex-alignment"> - .settings-box.indented { - -webkit-margin-start: 20px; - align-self: stretch; - padding: 0; - } - - :host { - --paper-tabs-selection-bar-color: var(--paper-blue-500); - } - - .display-tabs { - width: 100%; - } - - display-layout { - align-self: stretch; - flex: 1 1; - height: 300px; - margin: 10px; - } - - .textarea { - margin: 10px 0; - } - - .settings-box > paper-button:first-child { - -webkit-padding-start: 0 - } - - paper-tab { - text-transform: uppercase; - } - - #controlsDiv > .settings-box:first-of-type { - border-top: none; - } - - #nightLightSlider { - margin-top: 20px; - } + .indented { + -webkit-margin-start: var(--cr-section-indent-padding); + align-self: stretch; + padding: 0; + } + + .display-tabs { + width: 100%; + } + + display-layout { + align-self: stretch; + flex: 1 1; + height: 300px; + margin: 10px; + } + + .text-area { + margin: 10px 0; + } + + .settings-box > paper-button:first-child { + -webkit-padding-start: 0 + } + + paper-tab { + text-transform: uppercase; + } + + #controlsDiv > .settings-box:first-of-type { + border-top: none; + } + + #nightLightSlider { + flex-grow: 1; + margin-top: 20px; + } </style> <div class="settings-box first layout vertical self-stretch"> <h2 class="layout self-start"> @@ -108,7 +106,7 @@ <div class="settings-box indented two-line"> <div class="start"> <div id="displayUnifiedDesktopCheckboxLabel"> - $i18n{displayUnfiedDesktop} + $i18n{displayUnifiedDesktop} </div> <div class="secondary"> [[getUnifiedDesktopText_(unifiedDesktopMode_)]] @@ -141,13 +139,13 @@ </template> <div class="settings-box indented two-line"> - <div class="start textarea layout vertical"> + <div class="start text-area layout vertical"> <div>$i18n{displayResolutionTitle}</div> <div class="secondary layout self-start"> [[getResolutionText_(selectedDisplay, selectedModePref_.value)]] </div> </div> - <settings-slider disabled="[[!enableSetResolution_(selectedDisplay)]]" + <settings-slider disabled="[[selectedDisplay.isTabletMode]]" tick-values="[[modeValues_]]" pref="{{selectedModePref_}}" on-change="onSelectedModeChange_"> </settings-slider> @@ -155,11 +153,12 @@ <template is="dom-if" if="[[!unifiedDesktopMode_]]" restamp> <div class="settings-box indented"> - <div id="displayOrientation" class="start textarea"> + <div id="displayOrientation" class="start text-area"> $i18n{displayOrientation} </div> <div class="md-select-wrapper"> <select class="md-select" value="[[selectedDisplay.rotation]]" + disabled="[[selectedDisplay.isTabletMode]]" aria-labelledby="displayOrientation" on-change="onOrientationChange_"> <option value="0">$i18n{displayOrientationStandard}</option> @@ -172,19 +171,10 @@ </div> </template> - <div id="overscan" class="settings-box indented two-line" - on-tap="onOverscanTap_" hidden$="[[selectedDisplay.isInternal]]" - actionable> - <div class="start"> - $i18n{displayOverscanPageTitle} - <div class="secondary" id="displayOverscanSecondary"> - $i18n{displayOverscanPageText} - </div> - </div> - <button class="subpage-arrow" is="paper-icon-button-light" - aria-label="$i18n{displayOverscanPageTitle}" - aria-describedby="displayOverscanSecondary"></button> - </div> + <button is="cr-link-row" icon-class="subpage-arrow" class="indented hr" + id="overscan" label="$i18n{displayOverscanPageTitle}" + sub-label="$i18n{displayOverscanPageText}" on-tap="onOverscanTap_"> + </button> <settings-display-overscan-dialog id="displayOverscan" display-id="{{overscanDisplayId}}" @@ -219,7 +209,7 @@ class="settings-box continuation start layout vertical"> <!-- Color temperature slider --> <div class="settings-box indented continuation"> - <div class="start textarea" id="colorTemperatureLabel"> + <div class="start text-area" id="colorTemperatureLabel"> $i18n{displayNightLightTemperatureLabel} </div> <settings-slider id="colorTemperatureSlider" @@ -231,7 +221,7 @@ </div> <!-- Schedule settings --> <div class="settings-box indented"> - <div id="nightLightScheduleLabel" class="start textarea"> + <div id="nightLightScheduleLabel" class="start text-area"> $i18n{displayNightLightScheduleLabel} </div> <settings-dropdown-menu @@ -244,9 +234,9 @@ <!-- Custom schedule slider --> <div class="settings-box indented continuation"> <iron-collapse id="nightLightCustomScheduleCollapse" - class="start textarea layout vertical" + class="start text-area layout vertical" opened="[[shouldOpenCustomScheduleCollapse_]]"> - <div class="settings-box indented continuation"> + <div class="settings-box continuation self-stretch"> <night-light-slider id="nightLightSlider" prefs="{{prefs}}"> </night-light-slider> </div> diff --git a/chromium/chrome/browser/resources/settings/device_page/night_light_slider.html b/chromium/chrome/browser/resources/settings/device_page/night_light_slider.html index 247f30fa4f7..84bd90b309d 100644 --- a/chromium/chrome/browser/resources/settings/device_page/night_light_slider.html +++ b/chromium/chrome/browser/resources/settings/device_page/night_light_slider.html @@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.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/polymer/v1_0/iron-resizable-behavior/iron-resizable-behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-behaviors/paper-inky-focus-behavior.html"> <link rel="import" href="../prefs/prefs_behavior.html"> @@ -16,7 +17,7 @@ display: inline-block; position: relative; user-select: none; - width: 570px; + width: 100%; } #sliderBar { @@ -143,11 +144,13 @@ <div id="labelContainer"> <div id="startLabel" class="label" aria-label="$i18n{displayNightLightStartTime}"> - [[startTime_]] + [[getTimeString_(prefs.ash.night_light.custom_start_time.value, + shouldUse24Hours_)]] </div> <div id="endLabel" class="label" aria-label="$i18n{displayNightLightStopTime}"> - [[endTime_]] + [[getTimeString_(prefs.ash.night_light.custom_end_time.value, + shouldUse24Hours_)]] </div> </div> <div id="sliderBar"> diff --git a/chromium/chrome/browser/resources/settings/device_page/night_light_slider.js b/chromium/chrome/browser/resources/settings/device_page/night_light_slider.js index 84210288d1f..239a9f42f04 100644 --- a/chromium/chrome/browser/resources/settings/device_page/night_light_slider.js +++ b/chromium/chrome/browser/resources/settings/device_page/night_light_slider.js @@ -22,21 +22,16 @@ Polymer({ behaviors: [ PrefsBehavior, Polymer.IronA11yKeysBehavior, + Polymer.IronResizableBehavior, Polymer.PaperInkyFocusBehavior, ], properties: { /** - * The start knob time as a string to be shown on the start label bubble. + * Whether the element is ready and fully rendered. * @private */ - startTime_: String, - - /** - * The end knob time as a string to be shown on the end label bubble. - * @private - */ - endTime_: String, + isReady_: Boolean, /** * Whether the window is in RTL locales. @@ -52,9 +47,13 @@ Polymer({ shouldUse24Hours_: Boolean, }, + listeners: { + 'iron-resize': 'onResize_', + }, + observers: [ - 'customTimesChanged_(prefs.ash.night_light.custom_start_time.*, ' + - 'prefs.ash.night_light.custom_end_time.*)', + 'updateKnobs_(prefs.ash.night_light.custom_start_time.*, ' + + 'prefs.ash.night_light.custom_end_time.*, isRTL_, isReady_)', 'hourFormatChanged_(prefs.settings.clock.use_24hour_clock.*)', ], @@ -72,11 +71,6 @@ Polymer({ /** @override */ attached: function() { - this.isRTL_ = window.getComputedStyle(this).direction == 'rtl'; - }, - - /** @override */ - ready: function() { // Build the legend markers. var markersContainer = this.$.markersContainer; var width = markersContainer.offsetWidth; @@ -86,6 +80,25 @@ Polymer({ markersContainer.appendChild(marker); marker.style.left = (i * 100 / HOURS_PER_DAY) + '%'; } + + this.isRTL_ = window.getComputedStyle(this).direction == 'rtl'; + + this.async(function() { + // This is needed to make sure that the positions of the knobs and their + // label bubbles are correctly updated when the display settings page is + // opened for the first time after login. The page need to be fully + // rendered. + this.isReady_ = true; + }); + }, + + /** + * Invoked when the element is resized and the knobs positions need to be + * updated. + * @private + */ + onResize_: function() { + this.updateKnobs_(); }, /** @@ -96,9 +109,6 @@ Polymer({ hourFormatChanged_: function() { this.shouldUse24Hours_ = /** @type {boolean} */ ( this.getPref('settings.clock.use_24hour_clock').value); - - // Refresh the slider. - this.customTimesChanged_(); }, /** @@ -265,28 +275,28 @@ Polymer({ * 00:00) to its language-sensitive time string representation. * @param {number} offsetMinutes The time of day represented as the number of * minutes from 00:00. + * @param {boolean} shouldUse24Hours Whether to use the 24-hour time format. * @return {string} * @private */ - offsetMinutesToTimeString_: function(offsetMinutes) { + getTimeString_: function(offsetMinutes, shouldUse24Hours) { var hour = Math.floor(offsetMinutes / 60); var minute = Math.floor(offsetMinutes % 60); - return this.getLocaleTimeString_(hour, minute, this.shouldUse24Hours_); + return this.getLocaleTimeString_(hour, minute, shouldUse24Hours); }, /** - * Handles changes in the start and end times prefs. + * Using the current start and end times prefs, this function updates the + * knobs and their label bubbles and refreshes the slider. * @private */ - customTimesChanged_: function() { + updateKnobs_: function() { var startOffsetMinutes = /** @type {number} */ ( this.getPref('ash.night_light.custom_start_time').value); - this.startTime_ = this.offsetMinutesToTimeString_(startOffsetMinutes); this.updateKnobLeft_(this.$.startKnob, startOffsetMinutes); var endOffsetMinutes = /** @type {number} */ ( this.getPref('ash.night_light.custom_end_time').value); - this.endTime_ = this.offsetMinutesToTimeString_(endOffsetMinutes); this.updateKnobLeft_(this.$.endKnob, endOffsetMinutes); this.refresh_(); }, diff --git a/chromium/chrome/browser/resources/settings/direction_delegate.html b/chromium/chrome/browser/resources/settings/direction_delegate.html deleted file mode 100644 index be62c99a60c..00000000000 --- a/chromium/chrome/browser/resources/settings/direction_delegate.html +++ /dev/null @@ -1,4 +0,0 @@ -<link rel="import" href="i18n_setup.html"> -<link rel="import" href="chrome://resources/html/assert.html"> -<link rel="import" href="chrome://resources/html/cr.html"> -<script src="direction_delegate.js"></script> diff --git a/chromium/chrome/browser/resources/settings/direction_delegate.js b/chromium/chrome/browser/resources/settings/direction_delegate.js deleted file mode 100644 index 6d5275337e9..00000000000 --- a/chromium/chrome/browser/resources/settings/direction_delegate.js +++ /dev/null @@ -1,29 +0,0 @@ -// 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. - -cr.exportPath('settings'); - -cr.define('settings', function() { - /** @interface */ - class DirectionDelegate { - /** - * @return {boolean} Whether the direction of the settings UI is - * right-to-left. - */ - isRtl() {} - } - - /** @implements {settings.DirectionDelegate} */ - class DirectionDelegateImpl { - /** @override */ - isRtl() { - return loadTimeData.getString('textdirection') == 'rtl'; - } - } - - return { - DirectionDelegate: DirectionDelegate, - DirectionDelegateImpl: DirectionDelegateImpl, - }; -}); diff --git a/chromium/chrome/browser/resources/settings/icons.html b/chromium/chrome/browser/resources/settings/icons.html index c49d8fa899c..61224dceb7b 100644 --- a/chromium/chrome/browser/resources/settings/icons.html +++ b/chromium/chrome/browser/resources/settings/icons.html @@ -59,6 +59,8 @@ List icons here rather than importing large sets of (e.g. Polymer) icons. <g id="content-copy"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"></path></g> <if expr="chromeos"> <g id="computer"><path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"></path></g> + <g id="devices-other"><path d="M3 6h18V4H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4v-2H3V6zm10 6H9v1.78c-.61.55-1 1.33-1 2.22s.39 1.67 1 2.22V20h4v-1.78c.61-.55 1-1.34 1-2.22s-.39-1.67-1-2.22V12zm-2 5.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM22 8h-6c-.5 0-1 .5-1 1v10c0 .5.5 1 1 1h6c.5 0 1-.5 1-1V9c0-.5-.5-1-1-1zm-1 10h-4v-8h4v8z"></path></g> + </if> <g id="done"><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"></path></g> <g id="error"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></g> diff --git a/chromium/chrome/browser/resources/settings/images/settings_icon_visibility.svg b/chromium/chrome/browser/resources/settings/images/settings_icon_visibility.svg deleted file mode 100644 index af7b42a957d..00000000000 --- a/chromium/chrome/browser/resources/settings/images/settings_icon_visibility.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#757575" preserveAspectRatio="xMidYMid meet"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"></path></svg>
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/settings/images/settings_icon_visibility_off.svg b/chromium/chrome/browser/resources/settings/images/settings_icon_visibility_off.svg deleted file mode 100644 index bce77739f74..00000000000 --- a/chromium/chrome/browser/resources/settings/images/settings_icon_visibility_off.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#757575" preserveAspectRatio="xMidYMid meet"><path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/></svg> diff --git a/chromium/chrome/browser/resources/settings/internet_page/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/internet_page/compiled_resources2.gyp index 8cbb7f9805e..18f9191b2ff 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/internet_page/compiled_resources2.gyp @@ -22,6 +22,7 @@ 'target_name': 'internet_config', 'dependencies': [ '../compiled_resources2.gyp:route', + '<(DEPTH)/ui/webui/resources/cr_components/chromeos/network/compiled_resources2.gyp:network_config', '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', @@ -71,19 +72,6 @@ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'network_config_input', - 'dependencies': [], - 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { - 'target_name': 'network_config_select', - 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', - ], - 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { 'target_name': 'network_proxy_section', 'dependencies': [ '../compiled_resources2.gyp:route', diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_config.html b/chromium/chrome/browser/resources/settings/internet_page/internet_config.html index 6c2a1f2fdd1..fd8d716df89 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_config.html +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_config.html @@ -1,14 +1,12 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/network/network_config.html"> <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_icon.html"> <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> <link rel="import" href="chrome://resources/html/i18n_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-button/paper-button.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> <link rel="import" href="internet_shared_css.html"> -<link rel="import" href="network_config_input.html"> -<link rel="import" href="network_config_select.html"> <dom-module id="settings-internet-config"> <template> @@ -20,7 +18,7 @@ <div class="start layout horizontal center"> <cr-network-icon network-state="[[networkProperties_]]" is-list-item> </cr-network-icon> - <div class="title">[[title_]]</div> + <div class="title">[[getTitle_(networkProperties_.*)]]</div> </div> <div id="buttonDiv"> <paper-button class="secondary-button" on-tap="onCancelTap_"> @@ -28,79 +26,27 @@ </paper-button> <template is="dom-if" if="[[guid_]]"> <paper-button class="primary-button" on-tap="onSaveTap_" - disabled="[[!saveIsEnabled_(configProperties_.*)]]"> + disabled="[[!enableSave_]]"> $i18n{save} </paper-button> </template> <template is="dom-if" if="[[!guid_]]"> <paper-button class="primary-button" on-tap="onConnectTap_" - disabled="[[!connectIsEnabled_(configProperties_.*)]]"> + disabled="[[!enableConnect_]]"> $i18n{networkButtonConnect} </paper-button> </template> </div> </div> - <!-- WiFi --> - <template is="dom-if" - if="[[isType_(NetworkType_.WI_FI, networkProperties_.Type)]]"> - <div class="settings-box"> - <div id="shareLabel" class="start">$i18n{networkConfigShare}</div> - <paper-toggle-button id="share" checked="{{shareNetwork_}}" - disabled="[[!shareIsEnabled_(guid_, configProperties_.*)]]" - aria-labelledby="shareLabel"> - </paper-toggle-button> - </div> - <network-config-input id="ssid" label="$i18n{OncWiFi-SSID}" - value="{{configProperties_.WiFi.SSID}}" disabled="[[guid_]]"> - </network-config-input> - <network-config-select id="security" label="$i18n{OncWiFi-Security}" - value="{{configProperties_.WiFi.Security}}" disabled="[[guid_]]" - items="[[securityItems_]]" onc-prefix="WiFi.Security"> - </network-config-select> - <network-config-input label="$i18n{OncWiFi-Passphrase}" - value="{{configProperties_.WiFi.Passphrase}}" - hidden="[[!configRequiresPassphrase_(configProperties_.*)]]"> - </network-config-input> - </template> - - <!-- EAP (WiFi, WiMAX, Ethernet) --> - <template is="dom-if" if="[[showEap_]]"> - <network-config-select id="outer" label="$i18n{OncEAP-Outer}" - value="{{eapProperties_.Outer}}" items="[[eapOuterItems_]]" - onc-prefix="EAP.Outer" hidden="[[!showEap_.Outer]]"> - </network-config-select> - <network-config-select id="inner" label="$i18n{OncEAP-Inner}" - value="{{eapProperties_.Inner}}" - items="[[getEapInnerItems_(eapProperties_.Outer)]]" - onc-prefix="EAP.Inner" hidden="[[!showEap_.Inner]]"> - </network-config-select> - <network-config-input label="$i18n{OncEAP-SubjectMatch}" - value="{{eapProperties_.SubjectMatch}}" - hidden="[[!showEap_.SubjectMatch]]"> - </network-config-input> - <network-config-input label="$i18n{OncEAP-Identity}" - value="{{eapProperties_.Identity}}"> - </network-config-input> - <network-config-input label="$i18n{OncEAP-Password}" - value="{{eapProperties_.Password}}" hidden="[[!showEap_.Password]]"> - </network-config-input> - <network-config-input label="$i18n{OncEAP-AnonymousIdentity}" - value="{{eapProperties_.AnonymousIdentity}}" - hidden="[[!showEap_.AnonymousIdentity]]"> - </network-config-input> - <div class="settings-box"> - <div id="saveCredentialsLabel" class="start"> - $i18n{networkConfigSaveCredentials} - </div> - <paper-toggle-button checked="{{eapProperties_.SaveCredentials}}" - aria-labelledby="saveCredentialsLabel"> - </paper-toggle-button> - </div> - </template> - - <!-- TODO(stevenjb): Error message for config or cert failure --> - + <div class="settings-box"> + <network-config id="networkConfig" class="flex" + networking-private="[[networkingPrivate]]" + network-properties="{{networkProperties_}}" + enable-connect="{{enableConnect_}}" enable-save="{{enableSave_}}" + on-close="close_"> + </network-config> + </div> </template> <script src="internet_config.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_config.js b/chromium/chrome/browser/resources/settings/internet_page/internet_config.js index 4760b53a5e0..a74aa471f2e 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_config.js +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_config.js @@ -4,8 +4,7 @@ /** * @fileoverview - * 'settings-internet-config' provides configuration of authentication - * properties for new and existing networks. + * 'settings-internet-config' is a Settings wrapper for network-config. */ Polymer({ is: 'settings-internet-config', @@ -26,167 +25,21 @@ Polymer({ */ guid_: String, - /** - * The current properties if an existing network being configured. - * This will be undefined when configuring a new network. - * @private {!chrome.networkingPrivate.NetworkProperties|undefined} - */ - networkProperties_: Object, - - /** Set if |guid_| is not empty once networkProperties_ are received. */ - propertiesReceived_: Boolean, - - /** Set once properties have been sent; prevents multiple saves. */ - propertiesSent_: Boolean, - - /** - * The configuration properties for the network. |configProperties_.Type| - * will always be defined as the network type being configured. - * @private {!chrome.networkingPrivate.NetworkConfigProperties} - */ - configProperties_: Object, - - /** - * Reference to the EAP properties for the current type or null if all EAP - * properties should be hidden (e.g. WiFi networks with non EAP Security). - * Note: even though this references an entry in configProperties_, we - * need to send a separate notification when it changes for data binding - * (e.g. by using 'set'). - * @private {?chrome.networkingPrivate.EAPProperties} - */ - eapProperties_: { - type: Object, - value: null, - }, - - /** - * The title to display (network name or type). - * @private - */ - title_: { - type: String, - computed: 'computeTitle_(networkProperties_)', - }, - - /** - * Whether this network should be shared with other users of the device. - * @private - */ - shareNetwork_: { - type: Boolean, - value: true, - }, + /** @private */ + enableConnect_: String, - /** - * Saved security value, used to detect when Security changes. - * @private - */ - savedSecurity_: String, + /** @private */ + enableSave_: String, /** - * Dictionary of boolean values determining which EAP properties to show, - * or null to hide all EAP settings. - * @type {?{ - * Outer: boolean, - * Inner: boolean, - * ServerCA: boolean, - * SubjectMatch: boolean, - * UserCert: boolean, - * Password: boolean, - * AnonymousIdentity: boolean, - * }} - * @private + * The current properties if an existing network is being configured, or + * a minimal subset for a new network. Note: network-config may modify + * this (specifically .name). + * @private {!chrome.networkingPrivate.NetworkProperties} */ - showEap_: { - type: Object, - value: null, - }, - - /** - * Object providing network type values for data binding. Note: Currently - * we only support WiFi, but support for other types will be following - * shortly. - * @const - * @private - */ - NetworkType_: { - type: Object, - value: { - ETHERNET: CrOnc.Type.ETHERNET, - VPN: CrOnc.Type.VPN, - WI_FI: CrOnc.Type.WI_FI, - WI_MAX: CrOnc.Type.WI_MAX, - }, - readOnly: true - }, - - /** - * Array of values for the WiFi Security dropdown. - * @type {!Array<string>} - * @const - * @private - */ - securityItems_: { - type: Array, - readOnly: true, - value: [ - CrOnc.Security.NONE, CrOnc.Security.WEP_PSK, CrOnc.Security.WPA_PSK, - CrOnc.Security.WPA_EAP - ], - }, - - /** - * Array of values for the EAP Method (Outer) dropdown. - * @type {!Array<string>} - * @const - * @private - */ - eapOuterItems_: { - type: Array, - readOnly: true, - value: [ - CrOnc.EAPType.LEAP, CrOnc.EAPType.PEAP, CrOnc.EAPType.EAP_TLS, - CrOnc.EAPType.EAP_TTLS - ], - }, - - /** - * Array of values for the EAP EAP Phase 2 authentication (Inner) dropdown - * when the Outer type is PEAP. - * @type {!Array<string>} - * @const - * @private - */ - eapInnerItemsPeap_: { - type: Array, - readOnly: true, - value: ['Automatic', 'MD5', 'MSCHAPv2'], - }, - - /** - * Array of values for the EAP EAP Phase 2 authentication (Inner) dropdown - * when the Outer type is EAP-TTLS. - * @type {!Array<string>} - * @const - * @private - */ - eapInnerItemsTtls_: { - type: Array, - readOnly: true, - value: ['Automatic', 'MD5', 'MSCHAP', 'MSCHAPv2', 'PAP', 'CHAP', 'GTC'], - }, + networkProperties_: Object, }, - observers: [ - 'updateConfigProperties_(networkProperties_)', - 'updateWiFiSecurity_(configProperties_.WiFi.Security)', - 'updateEapOuter_(eapProperties_.Outer)', - 'updateShowEap_(eapProperties_.*)', - ], - - /** @const */ - MIN_PASSPHRASE_LENGTH: 5, - /** * settings.RouteObserverBehavior * @param {!settings.Route} route @@ -196,10 +49,6 @@ Polymer({ if (route != settings.routes.NETWORK_CONFIG) return; - this.propertiesSent_ = false; - this.savedSecurity_ = ''; - this.showEap_ = null; - var queryParams = settings.getQueryParameters(); this.guid_ = queryParams.get('guid') || ''; @@ -207,17 +56,24 @@ Polymer({ // configurations until the current properties are loaded. var name = queryParams.get('name') || ''; var typeParam = queryParams.get('type'); - var type = typeParam ? CrOnc.getValidType(typeParam) : CrOnc.Type.WI_FI; + var type = (typeParam && CrOnc.getValidType(typeParam)) || CrOnc.Type.WI_FI; assert(type && type != CrOnc.Type.ALL); this.networkProperties_ = { GUID: this.guid_, Name: name, Type: type, }; - if (this.guid_) { - this.networkingPrivate.getProperties( - this.guid_, this.getPropertiesCallback_.bind(this)); - } + + // First focus this page (which will focus a button), then init the config + // element which will focus an enabled element if any. + this.focus(); + this.$.networkConfig.init(); + }, + + focus() { + var e = this.$$('paper-button:not([disabled])'); + assert(e); // The 'cancel' button should never be disabled. + e.focus(); }, /** @private */ @@ -227,322 +83,26 @@ Polymer({ }, /** - * networkingPrivate.getProperties callback. - * @param {!chrome.networkingPrivate.NetworkProperties} properties - * @private - */ - getPropertiesCallback_: function(properties) { - if (!properties) { - // If |properties| is null, the network no longer exists; close the page. - console.error('Network no longer exists: ' + this.guid_); - this.close_(); - return; - } - this.propertiesReceived_ = true; - this.networkProperties_ = properties; - - // Set the current shareNetwork_ value when porperties are received. - var source = this.networkProperties_.Source; - this.shareNetwork_ = - source == CrOnc.Source.DEVICE || source == CrOnc.Source.DEVICE_POLICY; - }, - - /** * @return {string} * @private */ - computeTitle_: function() { + getTitle_: function() { return this.networkProperties_.Name || this.i18n('OncType' + this.networkProperties_.Type); }, - /** - * Updates the config properties when |this.networkProperties_| changes. - * This gets called once when navigating to the page when default properties - * are set, and again for existing networks when the properties are received. - * @private - */ - updateConfigProperties_: function() { - var properties = this.networkProperties_; - var configProperties = - /** @type {chrome.networkingPrivate.NetworkConfigProperties} */ ({ - Name: properties.Name || '', - Type: properties.Type, - }); - if (properties.Type == CrOnc.Type.WI_FI) { - if (properties.WiFi) { - configProperties.WiFi = { - AutoConnect: properties.WiFi.AutoConnect, - EAP: Object.assign({}, properties.WiFi.EAP), - Passphrase: properties.WiFi.Passphrase, - SSID: properties.WiFi.SSID, - Security: properties.WiFi.Security - }; - } else { - configProperties.WiFi = { - AutoConnect: false, - SSID: '', - Security: CrOnc.Security.NONE, - }; - } - // updateWiFiSecurity_ will ensure that EAP properties are set correctly. - } else if (properties.Type == CrOnc.Type.ETHERNET) { - if (properties.Ethernet) { - configProperties.Ethernet = { - AutoConnect: properties.Ethernet.AutoConnect, - EAP: Object.assign({}, properties.Ethernet.EAP), - }; - configProperties.Ethernet.EAP.Outer = - configProperties.Ethernet.EAP.Outer || CrOnc.EAPType.LEAP; - } else { - configProperties.Ethernet = { - AutoConnect: false, - }; - } - } else if (properties.Type == CrOnc.Type.WI_MAX) { - if (properties.WiMAX) { - configProperties.WiMAX = { - AutoConnect: properties.WiMAX.AutoConnect, - EAP: Object.assign({}, properties.WiMAX.EAP), - }; - // WiMAX has no EAP.Outer property, only Identity and Password. - } else { - configProperties.WiMAX = { - AutoConnect: false, - }; - } - } - this.configProperties_ = configProperties; - this.set('eapProperties_', this.getEap_(this.configProperties_)); - if (!this.eapProperties_) - this.showEap_ = null; - }, - - /** - * Ensures that the appropriate properties are set or deleted when the - * Security type changes. - * @private - */ - updateWiFiSecurity_: function() { - if (!this.configProperties_.WiFi) - return; // May get called when clearing the property. - var security = this.configProperties_.WiFi.Security || CrOnc.Security.NONE; - if (security == this.savedSecurity_) - return; - this.savedSecurity_ = security; - - if (!this.guid_) { - // Set the default share state for new configurations. - // TODO(stevenjb): also check login state. - this.shareNetwork_ = security == CrOnc.Security.NONE; - } - - if (security == CrOnc.Security.WPA_EAP) { - var eap = this.configProperties_.WiFi.EAP || {}; - eap.Outer = eap.Outer || CrOnc.EAPType.LEAP; - this.configProperties_.WiFi.EAP = eap; - this.set('eapProperties_', this.configProperties_.WiFi.EAP); - } else { - delete this.configProperties_.WiFi.EAP; - this.eapProperties_ = null; - } - }, - - /** - * Ensures that the appropriate EAP properties are created (or deleted when - * the EAP.Outer property changes. - * @private - */ - updateEapOuter_: function() { - var eap = this.eapProperties_; - if (!eap || !eap.Outer) - return; - var innerItems = this.getEapInnerItems_(eap.Outer); - if (innerItems.length > 0) { - if (!eap.Inner || innerItems.indexOf(eap.Inner) < 0) - this.set('eapProperties_.Inner', innerItems[0]); - } else { - this.set('eapProperties_.Inner', undefined); - } - }, - /** @private */ - updateShowEap_: function() { - if (!this.eapProperties_) { - this.showEap_ = null; - return; - } - var type = this.configProperties_.Type; - var outer = this.eapProperties_.Outer; - this.showEap_ = { - Outer: type != CrOnc.Type.WI_MAX, - Inner: outer == CrOnc.EAPType.PEAP || outer == CrOnc.EAPType.EAP_TTLS, - ServerCA: type != CrOnc.Type.WI_MAX && outer != CrOnc.EAPType.LEAP, - SubjectMatch: outer == CrOnc.EAPType.EAP_TLS, - UserCert: outer == CrOnc.EAPType.EAP_TLS, - Password: outer != CrOnc.EAPType.EAP_TLS, - AnonymousIdentity: - outer == CrOnc.EAPType.PEAP || outer == CrOnc.EAPType.EAP_TTLS, - }; - }, - - /** - * @param {!chrome.networkingPrivate.NetworkConfigProperties} properties - * @return {?chrome.networkingPrivate.EAPProperties} - * @private - */ - getEap_: function(properties) { - if (properties.WiFi) - return properties.WiFi.EAP || null; - if (properties.Ethernet) - return properties.Ethernet.EAP || null; - if (properties.WiMAX) - return properties.WiMAX.EAP || null; - return null; - }, - - /** - * @param {CrOnc.Type} type The type to compare against. - * @param {CrOnc.Type} networkType The current network type. - * @return {boolean} True if the network type matches 'type'. - * @private - */ - isType_: function(type, networkType) { - return type == networkType; - }, - - /** - * @return {boolean} - * @private - */ - saveIsEnabled_: function() { - return this.propertiesReceived_ && !this.propertiesSent_; - }, - - /** - * @return {boolean} - * @private - */ - connectIsEnabled_: function() { - if (this.propertiesSent_) - return false; - if (this.configProperties_.Type == CrOnc.Type.WI_FI) { - if (!this.get('WiFi.SSID', this.configProperties_)) - return false; - if (this.configRequiresPassphrase_()) { - var passphrase = this.get('WiFi.Passphrase', this.configProperties_); - if (!passphrase || passphrase.length < this.MIN_PASSPHRASE_LENGTH) - return false; - } - } - // TODO(stevenjb): Check certificates. - return true; - }, - - /** - * @return {boolean} - * @private - */ - shareIsEnabled_: function() { - if (this.networkProperties_.Source == CrOnc.Source.DEVICE || - this.networkProperties_.Source == CrOnc.Source.DEVICE_POLICY) { - return false; - } - // TODO(stevenjb): Check login state. - - if (this.configProperties_.Type == CrOnc.Type.WI_FI) { - var security = this.get('WiFi.Security', this.configProperties_); - if (!security || security == CrOnc.Security.NONE) { - return false; - } else if (security == CrOnc.Security.WPA_EAP) { - var outer = this.get('WiFi.EAP.Outer', this.configProperties_); - if (outer == CrOnc.EAPType.EAP_TLS) - return false; - } - // TODO(stevenjb): Check certificates. - } - return true; + onCancelTap_: function() { + this.close_(); }, /** @private */ onSaveTap_: function() { - assert(this.guid_); - if (this.propertiesSent_) - return; - this.propertiesSent_ = true; - var propertiesToSet = Object.assign({}, this.configProperties_); - propertiesToSet.GUID = this.guid_; - this.networkingPrivate.setProperties( - this.guid_, propertiesToSet, this.setPropertiesCallback_.bind(this)); - }, - - /** @private */ - setPropertiesCallback_: function() { - var error = chrome.runtime.lastError && chrome.runtime.lastError.message; - if (error) { - console.error( - 'Error setting network properties: ' + this.guid_ + ': ' + error); - } - this.close_(); + this.$.networkConfig.saveOrConnect(); }, /** @private */ onConnectTap_: function() { - assert(!this.guid_); - if (this.propertiesSent_) - return; - this.propertiesSent_ = true; - // Create the configuration, then connect to it in the callback. - this.networkingPrivate.createNetwork( - this.shareNetwork_, this.configProperties_, - this.createNetworkCallback_.bind(this)); - }, - - /** - * @param {string} guid - * @private - */ - createNetworkCallback_: function(guid) { - var error = chrome.runtime.lastError && chrome.runtime.lastError.message; - if (error) { - // TODO(stevenjb): Display error message. - console.error( - 'Error creating network type: ' + this.networkProperties_.Type + - ': ' + error); - return; - } - this.networkProperties_.GUID = guid; - this.fire('network-connect', {networkProperties: this.networkProperties_}); - this.close_(); - }, - - /** @private */ - onCancelTap_: function() { - this.close_(); - }, - - /** - * @return boolean - * @private - */ - configRequiresPassphrase_: function() { - if (this.configProperties_.Type != CrOnc.Type.WI_FI) - return false; - var security = this.get('WiFi.Security', this.configProperties_); - return security == CrOnc.Security.WEP_PSK || - security == CrOnc.Security.WPA_PSK; - }, - - /** - * @param {string} outer - * @return {!Array<string>} - * @private - */ - getEapInnerItems_: function(outer) { - if (outer == CrOnc.EAPType.PEAP) - return this.eapInnerItemsPeap_; - if (outer == CrOnc.EAPType.EAP_TTLS) - return this.eapInnerItemsTtls_; - return []; + this.$.networkConfig.saveOrConnect(); }, }); 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 1caee855099..ac1652041e5 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 @@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_components/chromeos/network/network_apnlist.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/network/network_choose_mobile.html"> <link rel="import" href="chrome://resources/cr_components/chromeos/network/network_ip_config.html"> <link rel="import" href="chrome://resources/cr_components/chromeos/network/network_nameservers.html"> <link rel="import" href="chrome://resources/cr_components/chromeos/network/network_property_list.html"> @@ -160,22 +161,22 @@ <!-- Choose Mobile Network (Cellular only). --> <template is="dom-if" if="[[showCellularChooseNetwork_(networkProperties)]]"> - <div class="settings-box"> - <div class="start">$i18n{networkChooseMobileDetail}</div> - <paper-button on-tap="onChooseMobileTap_"> - $i18n{networkChooseMobileButton} - </paper-button> + <div class="settings-box single-column stretch"> + <network-choose-mobile networking-private="[[networkingPrivate]]" + network-properties="[[networkProperties]]"> + </network-choose-mobile> </div> </template> <!-- Data roaming (Cellular only). --> <template is="dom-if" if="[[isCellular_(networkProperties)]]"> - <settings-toggle-button id="allowDataRoaming" - pref="{{prefs.cros.signed.data_roaming_enabled}}" - label="$i18n{networkAllowDataRoaming}"> - </settings-toggle-button> + <settings-toggle-button id="allowDataRoaming" + pref="{{prefs.cros.signed.data_roaming_enabled}}" + label="$i18n{networkAllowDataRoaming}"> + </settings-toggle-button> </template> <!-- SIM Info (Cellular only). --> - <template is="dom-if" if="[[showCellularSim_(networkProperties)]]"> + <template is="dom-if" if="[[showCellularSim_(networkProperties)]]" + restamp> <div class="settings-box single-column stretch"> <network-siminfo editable on-siminfo-change="onNetworkPropertyChange_" 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 598a2369ff2..03ebf640485 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 @@ -229,11 +229,10 @@ Polymer({ if (!this.didSetFocus_) { // Focus a button once the initial state is set. this.didSetFocus_ = true; - var button = this.$$('#titleDiv .primary-button:not([hidden])'); - if (!button) - button = this.$$('#titleDiv paper-button:not([hidden])'); - assert(button); // At least one button will always be visible. - button.focus(); + var button = this.$$('#titleDiv .primary-button:not([hidden])') || + this.$$('#titleDiv paper-button:not([hidden])'); + if (button) + button.focus(); } if (this.shouldShowConfigureWhenNetworkLoaded_ && @@ -371,6 +370,8 @@ Polymer({ * @private */ getStateText_: function(networkProperties) { + if (!networkProperties.ConnectionState) + return ''; return this.i18n('Onc' + networkProperties.ConnectionState); }, @@ -486,14 +487,14 @@ Polymer({ if (this.connectNotAllowed_(networkProperties, globalPolicy)) return false; var type = networkProperties.Type; - if (type == CrOnc.Type.CELLULAR) + if (type == CrOnc.Type.CELLULAR || type == CrOnc.Type.TETHER) return false; if ((type == CrOnc.Type.WI_FI || type == CrOnc.Type.WI_MAX) && networkProperties.ConnectionState != CrOnc.ConnectionState.NOT_CONNECTED) { return false; } - return this.isRemembered_(networkProperties); + return true; }, /** @@ -540,8 +541,9 @@ Polymer({ enableConnect_: function(networkProperties, defaultNetwork, globalPolicy) { if (!this.showConnect_(networkProperties, globalPolicy)) return false; - if (networkProperties.Type == CrOnc.Type.CELLULAR && - CrOnc.isSimLocked(networkProperties)) { + if ((networkProperties.Type == CrOnc.Type.CELLULAR) && + (CrOnc.isSimLocked(networkProperties) || + this.get('Cellular.Scanning', networkProperties))) { return false; } if (networkProperties.Type == CrOnc.Type.VPN && !defaultNetwork) @@ -607,12 +609,6 @@ Polymer({ this.networkingPrivate.startActivate(this.guid); }, - /** @private */ - onChooseMobileTap_: function() { - // TODO(stevenjb): Integrate ChooseMobileNetworkDialog with WebUI. - chrome.send('addNetwork', [this.networkProperties.Type]); - }, - /** @const {string} */ CR_EXPAND_BUTTON_TAG: 'CR-EXPAND-BUTTON', @@ -1018,8 +1014,8 @@ Polymer({ */ showCellularSim_: function(networkProperties) { return networkProperties.Type == CrOnc.Type.CELLULAR && - this.get('Cellular.Family', this.networkProperties) == - CrOnc.NetworkTechnology.GSM; + !!networkProperties.Cellular && + networkProperties.Cellular.Family != 'CDMA'; }, /** 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 a213bc61188..f77ea978261 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_page.html +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_page.html @@ -51,14 +51,16 @@ if="[[deviceIsEnabled_(deviceStates.WiFi)]]"> <div actionable class="list-item" on-tap="onAddWiFiTap_"> <div class="start">$i18n{internetAddWiFi}</div> - <button class="icon-external" is="paper-icon-button-light" + <button class$="[[getAddNetworkClass_('WiFi')]]" + is="paper-icon-button-light" aria-label="$i18n{internetAddWiFi}"> </button> </div> </template> <div actionable class="list-item" on-tap="onAddVPNTap_"> <div class="start">$i18n{internetAddVPN}</div> - <button class="icon-external" is="paper-icon-button-light" + <button class$="[[getAddNetworkClass_('VPN')]]" + is="paper-icon-button-light" aria-label="$i18n{internetAddVPN}"> </button> </div> 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 50000efea86..85c7a78c1c8 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_page.js +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_page.js @@ -277,6 +277,20 @@ Polymer({ }, /** + * @param {string} type + * @return {string} + * @private + */ + getAddNetworkClass_: function(type) { + if (loadTimeData.getBoolean('networkSettingsConfig')) { + if (type == CrOnc.Type.WI_FI) + return 'icon-add-wifi'; + return 'icon-add-circle'; + } + return 'icon-external'; + }, + + /** * @param {string} subpageType * @param {!Object<!CrOnc.DeviceStateProperties>|undefined} deviceStates * @return {!CrOnc.DeviceStateProperties|undefined} @@ -325,7 +339,10 @@ Polymer({ /** @private */ onAddVPNTap_: function() { - chrome.send('addNetwork', [CrOnc.Type.VPN]); + if (loadTimeData.getBoolean('networkSettingsConfig')) + this.showConfig_(CrOnc.Type.VPN); + else + chrome.send('addNetwork', [CrOnc.Type.VPN]); }, /** 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 8a9fbd27179..1d60703ee2c 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 @@ -4,6 +4,10 @@ <dom-module id="internet-shared"> <template> <style include="settings-shared"> + :root { + --network-control-width: 250px; + } + cr-network-icon { -webkit-padding-end: var(--settings-box-row-padding); } @@ -20,6 +24,7 @@ }; margin-bottom: 0; margin-top: -9px; + width: var(--network-control-width); } .indented { 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 92870ea7316..0434b0e14c0 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js @@ -354,10 +354,12 @@ Polymer({ /** @private */ onAddButtonTap_: function() { assert(this.deviceState); + var type = this.deviceState.Type; + assert(type != CrOnc.Type.CELLULAR); if (loadTimeData.getBoolean('networkSettingsConfig')) - this.fire('show-config', {GUID: '', Type: this.deviceState.Type}); + this.fire('show-config', {GUID: '', Type: type}); else - chrome.send('addNetwork', [this.deviceState.Type]); + chrome.send('addNetwork', [type]); }, /** diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_config_input.html b/chromium/chrome/browser/resources/settings/internet_page/network_config_input.html deleted file mode 100644 index 8ce5b88c82d..00000000000 --- a/chromium/chrome/browser/resources/settings/internet_page/network_config_input.html +++ /dev/null @@ -1,20 +0,0 @@ -<link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-input/iron-input.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html"> -<link rel="import" href="../settings_shared_css.html"> - -<dom-module id="network-config-input"> - <template> - <style include="settings-shared"> - </style> - - <div class="settings-box"> - <div id="label" class="start">[[label]]</div> - <paper-input-container no-label-float> - <input is="iron-input" value="{{value::change}}" - disabled="[[disabled]]" aria-labelledby="label"> - </paper-input-container> - </div> - </template> - <script src="network_config_input.js"></script> -</dom-module> diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_config_input.js b/chromium/chrome/browser/resources/settings/internet_page/network_config_input.js deleted file mode 100644 index 0d9aa62a1ff..00000000000 --- a/chromium/chrome/browser/resources/settings/internet_page/network_config_input.js +++ /dev/null @@ -1,21 +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. - -/** - * @fileoverview Polymer element for network configuration input fields. - */ -Polymer({ - is: 'network-config-input', - - properties: { - label: String, - - disabled: Boolean, - - value: { - type: String, - notify: true, - } - }, -}); diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_config_select.html b/chromium/chrome/browser/resources/settings/internet_page/network_config_select.html deleted file mode 100644 index f71400cff19..00000000000 --- a/chromium/chrome/browser/resources/settings/internet_page/network_config_select.html +++ /dev/null @@ -1,24 +0,0 @@ -<link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/html/md_select_css.html"> -<link rel="import" href="../settings_shared_css.html"> - -<dom-module id="network-config-select"> - <template> - <style include="settings-shared md-select"> - </style> - - <div class="settings-box"> - <div id="label" class="start">[[label]]</div> - <select class="md-select" disabled="[[disabled]]" - value="{{value::change}}" aria-labelledby="label"> - <template is="dom-repeat" items="[[items]]"> - <option value="[[item]]"> - [[getOncLabel_(item, oncPrefix)]] - </option> - </template> - </select> - </div> - - </template> - <script src="network_config_select.js"></script> -</dom-module> diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_config_select.js b/chromium/chrome/browser/resources/settings/internet_page/network_config_select.js deleted file mode 100644 index 23794bb400f..00000000000 --- a/chromium/chrome/browser/resources/settings/internet_page/network_config_select.js +++ /dev/null @@ -1,62 +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. - -/** - * @fileoverview Polymer element for network configuration selection menus. - */ -Polymer({ - is: 'network-config-select', - - behaviors: [I18nBehavior], - - properties: { - label: String, - - disabled: Boolean, - - /** - * Array of item values to select from. - * @type {!Array<string>} - */ - items: Array, - - /** Prefix used to look up ONC property names. */ - oncPrefix: String, - - /** Select item value */ - value: { - type: String, - notify: true, - }, - }, - - observers: ['updateSelected_(items, value)'], - - /** - * Ensure that the <select> value is updated when |items| or |value| changes. - * @private - */ - updateSelected_: function() { - // Wait for the dom-repeat to populate the <option> entries. - this.async(function() { - var select = this.$$('select'); - if (select.value != this.value) - select.value = this.value; - }); - }, - - /** - * @param {string} key - * @param {string} prefix - * @return {string} The text to display for the onc value. - * @private - */ - getOncLabel_: function(key, prefix) { - var oncKey = 'Onc' + prefix.replace(/\./g, '-') + '_' + key; - if (this.i18nExists(oncKey)) - return this.i18n(oncKey); - assertNotReached(); - return key; - }, -}); 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 f4cddf4cd3d..f5d88e89c41 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 @@ -9,7 +9,6 @@ <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/html/md_select_css.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-button/paper-button.html"> <link rel="import" href="../controls/extension_controlled_indicator.html"> @@ -21,7 +20,7 @@ <dom-module id="network-proxy-section"> <template> - <style include="internet-shared md-select cr-hidden-style iron-flex iron-flex-alignment"> + <style include="internet-shared cr-hidden-style iron-flex iron-flex-alignment"> cr-policy-network-indicator { -webkit-margin-end: 10px; } @@ -82,7 +81,9 @@ close-text="$i18n{close}" on-cancel="onAllowSharedDialogCancel_" on-close="onAllowSharedDialogClose_"> <div slot="title"> - $i18n{networkProxyAllowSharedWarningTitle} + [[getAllowSharedDialogTitle_(prefs.settings.use_shared_proxies.value, + '$i18n{networkProxyAllowSharedEnableWarningTitle}', + '$i18n{networkProxyAllowSharedDisableWarningTitle}')]] </div> <div slot="body"> $i18n{networkProxyAllowSharedWarningMessage} diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.js b/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.js index 4a637c97d29..ce303c1a8b1 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.js +++ b/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.js @@ -67,6 +67,17 @@ Polymer({ }, /** + * @param {boolean} allowShared + * @param {string} enableStr + * @param {string} disableStr + * @return {string} + * @private + */ + getAllowSharedDialogTitle_: function(allowShared, enableStr, disableStr) { + return allowShared ? disableStr : enableStr; + }, + + /** * @return {boolean} * @private */ @@ -91,7 +102,8 @@ Polymer({ * @private */ shouldShowAllowShared_: function(property) { - return !this.isControlled(property) && this.isShared_(); + return this.isShared_() && !this.isNetworkPolicyEnforced(property) && + !this.isExtensionControlled(property); }, /** diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_siminfo.js b/chromium/chrome/browser/resources/settings/internet_page/network_siminfo.js index cb0eb9cbd05..2ea134f2069 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/network_siminfo.js +++ b/chromium/chrome/browser/resources/settings/internet_page/network_siminfo.js @@ -71,6 +71,18 @@ Polymer({ sendSimLockEnabled_: false, + /** @override */ + detached: function() { + if (this.$.enterPinDialog.open) + this.$.enterPinDialog.close(); + if (this.$.changePinDialog.open) + this.$.changePinDialog.close(); + if (this.$.unlockPinDialog.open) + this.$.unlockPinDialog.close(); + if (this.$.unlockPukDialog.open) + this.$.unlockPukDialog.close(); + }, + /** @private */ networkPropertiesChanged_: function() { if (!this.networkProperties || !this.networkProperties.Cellular) @@ -135,6 +147,7 @@ Polymer({ * @private */ sendEnterPin_: function(event) { + event.stopPropagation(); var guid = (this.networkProperties && this.networkProperties.GUID) || ''; var pin = this.$.enterPin.value; if (!this.validatePin_(pin)) { @@ -162,9 +175,9 @@ Polymer({ * @private */ onChangePinTap_: function(event) { + event.stopPropagation(); if (!this.networkProperties || !this.networkProperties.Cellular) return; - event.stopPropagation(); this.error_ = ErrorType.NONE; this.$.changePinOld.value = ''; this.$.changePinNew1.value = ''; @@ -178,6 +191,7 @@ Polymer({ * @private */ sendChangePin_: function(event) { + event.stopPropagation(); var guid = (this.networkProperties && this.networkProperties.GUID) || ''; var newPin = this.$.changePinNew1.value; if (!this.validatePin_(newPin, this.$.changePinNew2.value)) @@ -219,6 +233,7 @@ Polymer({ * @private */ sendUnlockPin_: function(event) { + event.stopPropagation(); var guid = (this.networkProperties && this.networkProperties.GUID) || ''; var pin = this.$.unlockPin.value; if (!this.validatePin_(pin)) @@ -257,6 +272,7 @@ Polymer({ * @private */ sendUnlockPuk_: function(event) { + event.stopPropagation(); var guid = (this.networkProperties && this.networkProperties.GUID) || ''; var puk = this.$.unlockPuk.value; if (!this.validatePuk_(puk)) 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 6a9295eea59..b1a730a7e82 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 @@ -36,7 +36,8 @@ </style> <div actionable class="settings-box two-line" on-tap="onShowDetailsTap_"> <div id="details" no-flex$="[[showSimInfo_(deviceState)]]"> - <cr-network-icon network-state="[[activeNetworkState]]"> + <cr-network-icon network-state="[[activeNetworkState]]" + device-state="[[deviceState]]"> </cr-network-icon> <div class="flex"> <div id="networkName">[[getNetworkName_(activeNetworkState)]]</div> @@ -46,8 +47,8 @@ </div> </div> - <template is="dom-if" if="[[showSimInfo_(deviceState)]]"> - <network-siminfo editable + <template is="dom-if" if="[[showSimInfo_(deviceState)]]" restamp> + <network-siminfo editable on-tap="doNothing_" network-properties="[[getCellularState_(deviceState)]]" networking-private="[[networkingPrivate]]"> </network-siminfo> @@ -55,7 +56,8 @@ <template is="dom-if" if="[[showPolicyIndicator_(activeNetworkState)]]"> <cr-policy-indicator indicator-type="[[getIndicatorTypeForSource( - activeNetworkState.Source)]]"> + activeNetworkState.Source)]]" + on-tap="doNothing_"> </cr-policy-indicator> </template> 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 656d66606cd..9555d961bd8 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 @@ -85,8 +85,11 @@ Polymer({ return this.i18n('tetherEnableBluetooth'); } // Enabled or enabling states. - if (deviceState.State == CrOnc.DeviceState.ENABLED) - return CrOncStrings.networkListItemNotConnected; + if (deviceState.State == CrOnc.DeviceState.ENABLED) { + if (this.networkStateList.length > 0) + return CrOncStrings.networkListItemNotConnected; + return CrOncStrings.networkListItemNoNetwork; + } if (deviceState.State == CrOnc.DeviceState.ENABLING) return this.i18n('internetDeviceEnabling'); } @@ -306,4 +309,13 @@ Polymer({ // Make sure this does not propagate to onDetailsTap_. event.stopPropagation(); }, + + /** + * Make sure events in embedded components do not propagate to onDetailsTap_. + * @param {!Event} event + * @private + */ + doNothing_: function(event) { + event.stopPropagation(); + }, }); diff --git a/chromium/chrome/browser/resources/settings/languages_page/languages.js b/chromium/chrome/browser/resources/settings/languages_page/languages.js index 36da86dce34..47a7568bf09 100644 --- a/chromium/chrome/browser/resources/settings/languages_page/languages.js +++ b/chromium/chrome/browser/resources/settings/languages_page/languages.js @@ -23,6 +23,7 @@ var kLanguageCodeToTranslateCode = { 'zh-HK': 'zh-TW', 'zh-MO': 'zh-TW', 'zh-SG': 'zh-CN', + 'zh': 'zh-CH', }; // Some ISO 639 language codes have been renamed, e.g. "he" to "iw", but @@ -294,6 +295,8 @@ Polymer({ this.languages.prospectiveUILanguage) { continue; } + // This conversion primarily strips away the region part. + // For example "fr-CA" --> "fr". var translateCode = this.convertLanguageCodeForTranslate( this.languages.enabled[i].language.code); this.set( @@ -542,7 +545,6 @@ Polymer({ return; this.languageSettingsPrivate_.enableLanguage(languageCode); - this.disableTranslateLanguage(languageCode); }, /** @@ -573,7 +575,6 @@ Polymer({ // Remove the language from preferred languages. this.languageSettingsPrivate_.disableLanguage(languageCode); - this.enableTranslateLanguage(languageCode); }, /** @@ -668,8 +669,8 @@ Polymer({ * @param {string} languageCode */ enableTranslateLanguage: function(languageCode) { - languageCode = this.convertLanguageCodeForTranslate(languageCode); - this.deletePrefListItem('translate_blocked_languages', languageCode); + this.languageSettingsPrivate_.setEnableTranslationForLanguage( + languageCode, true); }, /** @@ -678,9 +679,8 @@ Polymer({ * @param {string} languageCode */ disableTranslateLanguage: function(languageCode) { - this.appendPrefListItem( - 'translate_blocked_languages', - this.convertLanguageCodeForTranslate(languageCode)); + this.languageSettingsPrivate_.setEnableTranslationForLanguage( + languageCode, false); }, /** diff --git a/chromium/chrome/browser/resources/settings/lifetime_browser_proxy.js b/chromium/chrome/browser/resources/settings/lifetime_browser_proxy.js index d97ac2d6b39..19bc02a2d69 100644 --- a/chromium/chrome/browser/resources/settings/lifetime_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/lifetime_browser_proxy.js @@ -15,8 +15,13 @@ cr.define('settings', function() { // First signs out current user and then performs a restart. signOutAndRestart() {} - // Triggers a factory reset. - factoryReset() {} + /** + * Triggers a factory reset. The parameter indicates whether to install a + * TPM firmware update (if available) after the reset. + * + * @param {boolean=} requestTpmFirmwareUpdate + */ + factoryReset(requestTpmFirmwareUpdate) {} // </if> } @@ -41,8 +46,8 @@ cr.define('settings', function() { } /** @override */ - factoryReset() { - chrome.send('factoryReset'); + factoryReset(requestTpmFirmwareUpdate) { + chrome.send('factoryReset', [requestTpmFirmwareUpdate]); } // </if> } 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 fc03d5eee73..b7a407def99 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.html +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.html @@ -1,20 +1,26 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> <link rel="import" href="../prefs/prefs.html"> +<link rel="import" href="../controls/settings_toggle_button.html"> <link rel="import" href="../settings_shared_css.html"> <dom-module id="settings-multidevice-page"> <template> - <style include="settings-shared"></style> + <style include="settings-shared"> + settings-toggle-button { + -webkit-padding-end: 0; + flex: 1; + } + </style> <div class="settings-box two-line"> - <div id="smsConnectToggleLabel" class="start"> - $i18n{smsConnect} - <div class="secondary">$i18n{smsConnectSummary}</div> + <div class="icon-container"> + <iron-icon icon="cr:sms-connect"></iron-icon> </div> - <paper-toggle-button checked="{{smsConnectToggleState_}}" - aria-labelledby="smsConnectToggleLabel"> - </paper-toggle-button> + <settings-toggle-button + pref="{{prefs.multidevice.sms_connect_enabled}}" + label="$i18n{smsConnect}" + sub-label="$i18n{smsConnectSummary}"> + </settings-toggle-button> </div> </template> <script src="multidevice_page.js"></script> 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 e402512aefe..0e1d72219d6 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.js +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.js @@ -16,22 +16,5 @@ Polymer({ type: Object, notify: true, }, - - /** - * Reflects the current state of the toggle buttons (in this page and the - * subpage). This will be set when the user changes the toggle. - * @private - */ - smsConnectToggleState_: { - type: Boolean, - observer: 'smsConnectToggleStateChanged_', - }, - }, - - /** @private */ - smsConnectToggleStateChanged_: function() { - // TODO(orenb): Switch from paper-toggle-button to settings-toggle-button, - // which will manage the underlying pref state, once the new pref has been - // implemented. Propagate here the pref value to the SMS connect component. }, }); diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources2.gyp index b61b25173ec..92e674c24f6 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources2.gyp @@ -61,6 +61,7 @@ 'dependencies': [ '../compiled_resources2.gyp:global_scroll_target_behavior', '<(DEPTH)/ui/webui/resources/cr_elements/cr_action_menu/compiled_resources2.gyp:cr_action_menu', + '<(DEPTH)/ui/webui/resources/cr_elements/cr_toast/compiled_resources2.gyp:cr_toast', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:focus_without_ink', 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 f84bd884948..28f9cec9ad3 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 @@ -1,6 +1,7 @@ <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_icons_css.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="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.html index 2bde5b23f8a..1d11c507149 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.html +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.html @@ -1,6 +1,6 @@ <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/cr_icons_css.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../focus_row_behavior.html"> <link rel="import" href="../settings_shared_css.html"> 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 a5293b941fa..55dd3fd8ed6 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 @@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html"> <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-flex-layout/iron-flex-layout-classes.html"> @@ -22,20 +23,10 @@ <settings-animated-pages id="pages" section="passwordsAndForms" focus-config="[[focusConfig_]]"> <neon-animatable route-path="default"> - <div class="settings-box first two-line"> - <div class="start two-line" on-tap="onAutofillTap_" actionable - id="autofillManagerButton"> - <div class="flex"> - $i18n{autofill} - <div class="secondary" id="autofillSecondary"> - $i18n{autofillDetail} - </div> - </div> - <button class="subpage-arrow" is="paper-icon-button-light" - aria-label="$i18n{autofill}" - aria-describedby="autofillSecondary"></button> - </div> - </div> + <button is="cr-link-row" icon-class="subpage-arrow" + id="autofillManagerButton" label="$i18n{autofill}" + sub-label="$i18n{autofillDetail}" on-tap="onAutofillTap_"> + </button> <div class="settings-box two-line"> <div class="start two-line" on-tap="onPasswordsTap_" actionable id="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 b621c11ca9a..9d9d0c6e90f 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 @@ -20,8 +20,7 @@ Polymer({ this.focusConfig_ = new Map(); if (settings.routes.AUTOFILL) { this.focusConfig_.set( - settings.routes.AUTOFILL.path, - '#autofillManagerButton .subpage-arrow'); + settings.routes.AUTOFILL.path, '#autofillManagerButton'); } if (settings.routes.MANAGE_PASSWORDS) { this.focusConfig_.set( 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 e9cdd80c0ee..fd2f647da10 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 @@ -1,10 +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/cr_elements/cr_toast/cr_toast.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"> <link rel="import" href="chrome://resources/html/i18n_behavior.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/polymer/v1_0/iron-list/iron-list.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../controls/settings_toggle_button.html"> @@ -27,6 +29,29 @@ -webkit-padding-end: calc( var(--cr-icon-ripple-size) + var(--cr-icon-button-margin-start)); } + + #undoLabel { + color: #fff; + display: flex; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + #undoButton { + -webkit-margin-end: 0; + -webkit-margin-start: 32px; + color: var(--google-blue-300); + font-weight: 500; + height: 32px; + min-width: 52px; + padding: 8px; + } + + #undoToast { + z-index: 1; + } </style> <settings-toggle-button id="passwordToggle" class="first primary-toggle" @@ -58,7 +83,18 @@ <span>$i18nRaw{managePasswordsLabel}</span> </div> <div class="settings-box first"> - <h2>$i18n{savedPasswordsHeading}</h2> + <h2 class="start">$i18n{savedPasswordsHeading}</h2> + <template is="dom-if" if="[[showImportExportPasswords_]]"> + <paper-button class="secondary-button header-aligned-button" + on-tap="onImportTap_" id="import"> + $i18n{import} + </paper-button> + <paper-button class="secondary-button header-aligned-button" + on-tap="onExportTap_" id="export" + disabled$="[[!hasSome_(savedPasswords)]]"> + $i18n{export} + </paper-button> + </template> </div> <div class="list-frame"> <div id="savedPasswordsHeading" class="list-item column-header" @@ -98,6 +134,12 @@ item="[[activePassword]]"> </password-edit-dialog> </template> + <cr-toast id="undoToast" duration="[[toastDuration_]]"> + <div id="undoLabel">$i18n{passwordDeleted}</div> + <paper-button id="undoButton" on-tap="onUndoButtonTap_"> + $i18n{undoRemovePassword} + </paper-button> + </cr-toast> <div class="settings-box block first"> <h2>$i18n{passwordExceptionsHeading}</h2> </div> 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 b426a015a25..9e5b23059c2 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 @@ -33,10 +33,10 @@ class PasswordManager { /** * Should remove the saved password and notify that the list has changed. - * @param {!PasswordManager.LoginPair} loginPair The saved password that - * should be removed from the list. No-op if |loginPair| is not found. + * @param {number} index The index for the password entry being removed. + * No-op if |index| is not in the list. */ - removeSavedPassword(loginPair) {} + removeSavedPassword(index) {} /** * Add an observer to the list of password exceptions. @@ -58,18 +58,34 @@ class PasswordManager { /** * Should remove the password exception and notify that the list has changed. - * @param {string} exception The exception that should be removed from the - * list. No-op if |exception| is not in the list. + * @param {number} index The index for the exception url entry being removed. + * No-op if |index| is not in the list. */ - removeException(exception) {} + removeException(index) {} + + /** + * Should undo the last saved password or exception removal and notify that + * the list has changed. + */ + undoRemoveSavedPasswordOrException() {} /** * Gets the saved password for a given login pair. - * @param {!PasswordManager.LoginPair} loginPair The saved password that - * should be retrieved. + * @param {number} index The index for password entry that should be + * retrieved. No-op if |index| is not in the list. * @param {function(!PasswordManager.PlaintextPasswordEvent):void} callback */ - getPlaintextPassword(loginPair, callback) {} + getPlaintextPassword(index, callback) {} + + /** + * Triggers the dialogue for importing passwords. + */ + importPasswords() {} + + /** + * Triggers the dialogue for exporting passwords. + */ + exportPasswords() {} } /** @typedef {chrome.passwordsPrivate.PasswordUiEntry} */ @@ -106,8 +122,8 @@ class PasswordManagerImpl { } /** @override */ - removeSavedPassword(loginPair) { - chrome.passwordsPrivate.removeSavedPassword(loginPair); + removeSavedPassword(index) { + chrome.passwordsPrivate.removeSavedPassword(index); } /** @override */ @@ -128,23 +144,37 @@ class PasswordManagerImpl { } /** @override */ - removeException(exception) { - chrome.passwordsPrivate.removePasswordException(exception); + removeException(index) { + chrome.passwordsPrivate.removePasswordException(index); + } + + /** @override */ + undoRemoveSavedPasswordOrException() { + chrome.passwordsPrivate.undoRemoveSavedPasswordOrException(); } /** @override */ - getPlaintextPassword(loginPair, callback) { + getPlaintextPassword(index, callback) { var listener = function(reply) { // Only handle the reply for our loginPair request. - if (reply.loginPair.urls.origin == loginPair.urls.origin && - reply.loginPair.username == loginPair.username) { + if (reply.index == index) { chrome.passwordsPrivate.onPlaintextPasswordRetrieved.removeListener( listener); callback(reply); } }; chrome.passwordsPrivate.onPlaintextPasswordRetrieved.addListener(listener); - chrome.passwordsPrivate.requestPlaintextPassword(loginPair); + chrome.passwordsPrivate.requestPlaintextPassword(index); + } + + /** @override */ + importPasswords() { + chrome.passwordsPrivate.importPasswords(); + } + + /** @override */ + exportPasswords() { + chrome.passwordsPrivate.exportPasswords(); } } @@ -162,7 +192,11 @@ var ExceptionEntryEntryEvent; Polymer({ is: 'passwords-section', - behaviors: [settings.GlobalScrollTargetBehavior, I18nBehavior], + behaviors: [ + I18nBehavior, + Polymer.IronA11yKeysBehavior, + settings.GlobalScrollTargetBehavior, + ], properties: { /** Preferences state. */ @@ -183,6 +217,15 @@ Polymer({ */ passwordExceptions: Array, + /** + * Duration of the undo toast in ms + * @private + */ + toastDuration_: { + type: Number, + value: 5000, + }, + /** @override */ subpageRoute: { type: Object, @@ -195,6 +238,22 @@ Polymer({ */ activePassword: Object, + + /** The target of the key bindings defined below. */ + keyEventTarget: { + type: Object, + value: () => document, + }, + + /** @private */ + showImportExportPasswords_: { + type: Boolean, + value: function() { + return loadTimeData.valueExists('showImportExportPasswords') && + loadTimeData.getBoolean('showImportExportPasswords'); + } + }, + /** @private */ showPasswordEditDialog_: Boolean, @@ -213,6 +272,15 @@ Polymer({ 'password-menu-tap': 'onPasswordMenuTap_', }, + keyBindings: { + // <if expr="is_macosx"> + 'meta+z': 'onUndoKeyBinding_', + // </if> + // <if expr="not is_macosx"> + 'ctrl+z': 'onUndoKeyBinding_', + // </if> + }, + /** * The element to return focus to, when the currently active dialog is * closed. @@ -264,6 +332,10 @@ Polymer({ setSavedPasswordsListener); this.passwordManager_.addExceptionListChangedListener( setPasswordExceptionsListener); + + Polymer.RenderStatus.afterNextRender(this, function() { + Polymer.IronA11yAnnouncer.requestAvailability(); + }); }, /** @override */ @@ -326,17 +398,31 @@ Polymer({ * @private */ onMenuRemovePasswordTap_: function() { - this.passwordManager_.removeSavedPassword(this.activePassword.loginPair); + this.passwordManager_.removeSavedPassword(this.activePassword.index); + this.fire('iron-announce', {text: this.$.undoLabel.textContent}); + this.$.undoToast.show(); /** @type {CrActionMenuElement} */ (this.$.menu).close(); }, + onUndoKeyBinding_: function(event) { + this.passwordManager_.undoRemoveSavedPasswordOrException(); + this.$.undoToast.hide(); + // Preventing the default is necessary to not conflict with a possible + // search action. + event.preventDefault(); + }, + + onUndoButtonTap_: function() { + this.passwordManager_.undoRemoveSavedPasswordOrException(); + this.$.undoToast.hide(); + }, /** * Fires an event that should delete the password exception. * @param {!ExceptionEntryEntryEvent} e The polymer event. * @private */ onRemoveExceptionButtonTap_: function(e) { - this.passwordManager_.removeException(e.model.item.urls.origin); + this.passwordManager_.removeException(e.model.item.index); }, /** @@ -355,6 +441,26 @@ Polymer({ this.activeDialogAnchor_ = target; }, + undoRemoveSavedPasswordOrException_: function(event) { + this.passwordManager_.undoRemoveSavedPasswordOrException(); + }, + + /** + * Fires an event that should trigger the password import process. + * @private + */ + onImportTap_: function() { + this.passwordManager_.importPasswords(); + }, + + /** + * Fires an event that should trigger the password export process. + * @private + */ + onExportTap_: function() { + this.passwordManager_.exportPasswords(); + }, + /** * Returns true if the list exists and has items. * @param {Array<Object>} list @@ -372,8 +478,7 @@ Polymer({ */ showPassword_: function(event) { this.passwordManager_.getPlaintextPassword( - /** @type {!PasswordManager.LoginPair} */ (event.detail.item.loginPair), - item => { + /** @type {!number} */ (event.detail.item.index), item => { event.detail.password = item.plaintextPassword; }); }, 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 2b642c9f527..82cbc58d44a 100644 --- a/chromium/chrome/browser/resources/settings/people_page/change_picture.html +++ b/chromium/chrome/browser/resources/settings/people_page/change_picture.html @@ -82,9 +82,9 @@ image-src="[[getImageSrc_(selectedItem_)]]" image-type="[[getImageType_(selectedItem_)]]" discard-image-label="$i18n{discardPhoto}" - flip-photo-label="$i18n{flipPhoto}" preview-alt-text="$i18n{previewAltText}" - take-photo-label="$i18n{takePhoto}"> + take-photo-label="$i18n{takePhoto}" + switch-mode-label="$i18n{switchMode}"> </cr-picture-pane> <div id="authorCredit" hidden="[[!isAuthorCreditShown_(selectedItem_)]]"> @@ -103,7 +103,8 @@ choose-file-label="$i18n{chooseFile}" old-image-label="$i18n{oldPhoto}" profile-image-label="$i18n{profilePhoto}" - take-photo-label="$i18n{takePhoto}"> + take-photo-label="$i18n{takePhoto}" + switch-mode-label="$i18n{switchMode}"> </cr-picture-list> </div> </template> diff --git a/chromium/chrome/browser/resources/settings/people_page/change_picture.js b/chromium/chrome/browser/resources/settings/people_page/change_picture.js index d41dc4c36db..b610c1d4ac2 100644 --- a/chromium/chrome/browser/resources/settings/people_page/change_picture.js +++ b/chromium/chrome/browser/resources/settings/people_page/change_picture.js @@ -58,8 +58,8 @@ Polymer({ listeners: { 'discard-image': 'onDiscardImage_', 'image-activate': 'onImageActivate_', - 'photo-flipped': 'onPhotoFlipped_', 'photo-taken': 'onPhotoTaken_', + 'switch-mode': 'onSwitchMode_', }, /** @private {?settings.ChangePictureBrowserProxy} */ @@ -209,11 +209,10 @@ Polymer({ * @param {!{detail: boolean}} event * @private */ - onPhotoFlipped_: function(event) { - var flipped = event.detail; - var flipMessageId = flipped ? 'photoFlippedAccessibleText' : - 'photoFlippedBackAccessibleText'; - announceAccessibleMessage(loadTimeData.getString(flipMessageId)); + onSwitchMode_: function(event) { + var videomode = event.detail; + announceAccessibleMessage(this.i18n( + videomode ? 'videoModeAccessibleText' : 'photoModeAccessibleText')); }, /** @private */ diff --git a/chromium/chrome/browser/resources/settings/people_page/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/people_page/compiled_resources2.gyp index d4e15b6a63e..d170ab31163 100644 --- a/chromium/chrome/browser/resources/settings/people_page/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/people_page/compiled_resources2.gyp @@ -150,6 +150,7 @@ 'dependencies': [ '../compiled_resources2.gyp:route', '../settings_page/compiled_resources2.gyp:settings_animated_pages', + '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/cr_picture/compiled_resources2.gyp:cr_png_behavior', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:icon', 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 b8d3d02ff26..aaac86af6d1 100644 --- a/chromium/chrome/browser/resources/settings/people_page/lock_screen.js +++ b/chromium/chrome/browser/resources/settings/people_page/lock_screen.js @@ -208,12 +208,14 @@ Polymer({ * @protected */ currentRouteChanged: function(newRoute, oldRoute) { - if (newRoute == settings.routes.LOCK_SCREEN && - this.fingerprintUnlockEnabled_ && this.fingerprintBrowserProxy_) { - this.fingerprintBrowserProxy_.getNumFingerprints().then( - numFingerprints => { - this.numFingerprints_ = numFingerprints; - }); + if (newRoute == settings.routes.LOCK_SCREEN) { + this.updateUnlockType(); + if (this.fingerprintUnlockEnabled_ && this.fingerprintBrowserProxy_) { + this.fingerprintBrowserProxy_.getNumFingerprints().then( + numFingerprints => { + this.numFingerprints_ = numFingerprints; + }); + } } if (this.shouldAskForPassword_(newRoute)) { diff --git a/chromium/chrome/browser/resources/settings/people_page/lock_state_behavior.js b/chromium/chrome/browser/resources/settings/people_page/lock_state_behavior.js index d40f58e59fc..b42fbee289c 100644 --- a/chromium/chrome/browser/resources/settings/people_page/lock_state_behavior.js +++ b/chromium/chrome/browser/resources/settings/people_page/lock_state_behavior.js @@ -42,11 +42,11 @@ var LockStateBehavior = { /** @override */ attached: function() { - this.boundOnActiveModesChanged_ = this.updateUnlockType_.bind(this); + this.boundOnActiveModesChanged_ = this.updateUnlockType.bind(this); this.quickUnlockPrivate_.onActiveModesChanged.addListener( this.boundOnActiveModesChanged_); - this.updateUnlockType_(); + this.updateUnlockType(); }, /** @override */ @@ -59,10 +59,8 @@ var LockStateBehavior = { * Updates the selected unlock type radio group. This function will get called * after preferences are initialized, after the quick unlock mode has been * changed, and after the lockscreen preference has changed. - * - * @private */ - updateUnlockType_: function() { + updateUnlockType: function() { this.quickUnlockPrivate_.getActiveModes(modes => { if (modes.includes(chrome.quickUnlockPrivate.QuickUnlockMode.PIN)) { this.hasPin = true; 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 bcb99fcf3de..cec6369f63b 100644 --- a/chromium/chrome/browser/resources/settings/people_page/people_page.html +++ b/chromium/chrome/browser/resources/settings/people_page/people_page.html @@ -26,6 +26,7 @@ <if expr="chromeos"> <link rel="import" href="change_picture.html"> +<link rel="import" href="chrome://resources/cr_elements/chromeos/cr_picture/cr_png_behavior.html"> <link rel="import" href="fingerprint_list.html"> <link rel="import" href="lock_screen.html"> <link rel="import" href="lock_state_behavior.html"> 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 1b1dec616e9..11610990843 100644 --- a/chromium/chrome/browser/resources/settings/people_page/people_page.js +++ b/chromium/chrome/browser/resources/settings/people_page/people_page.js @@ -12,7 +12,7 @@ Polymer({ behaviors: [ settings.RouteObserverBehavior, I18nBehavior, WebUIListenerBehavior, // <if expr="chromeos"> - LockStateBehavior, + CrPngBehavior, LockStateBehavior, // </if> ], @@ -156,6 +156,9 @@ 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. @@ -190,6 +193,18 @@ Polymer({ */ handleProfileInfo_: function(info) { this.profileName_ = info.name; + /** + * Extract first frame from image by creating a single frame PNG using + * url as input if base64 encoded and potentially animated. + */ + // <if expr="chromeos"> + if (info.iconUrl.startsWith('data:image/png;base64')) { + this.profileIconUrl_ = + CrPngBehavior.convertImageSequenceToPng([info.iconUrl]); + return; + } + // </if> + this.profileIconUrl_ = info.iconUrl; }, @@ -229,11 +244,6 @@ Polymer({ if (!this.syncStatus && syncStatus && !syncStatus.signedIn) chrome.metricsPrivate.recordUserAction('Signin_Impression_FromSettings'); - // <if expr="not chromeos"> - if (syncStatus.signedIn) - settings.ProfileInfoBrowserProxyImpl.getInstance().getProfileStatsCount(); - // </if> - if (!syncStatus.signedIn && this.showDisconnectDialog_) this.$$('#disconnectDialog').close(); diff --git a/chromium/chrome/browser/resources/settings/people_page/settings_icon_flip.svg b/chromium/chrome/browser/resources/settings/people_page/settings_icon_flip.svg deleted file mode 100644 index b95e714b522..00000000000 --- a/chromium/chrome/browser/resources/settings/people_page/settings_icon_flip.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#FFFFFF" preserveAspectRatio="xMidYMid meet"><path d="M15 21h2v-2h-2v2zm4-12h2V7h-2v2zM3 5v14c0 1.1.9 2 2 2h4v-2H5V5h4V3H5c-1.1 0-2 .9-2 2zm16-2v2h2c0-1.1-.9-2-2-2zm-8 20h2V1h-2v22zm8-6h2v-2h-2v2zM15 5h2V3h-2v2zm4 8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2z"></path></svg>
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/settings/people_page/user_list.js b/chromium/chrome/browser/resources/settings/people_page/user_list.js index f897fc9ba59..8158f7c31a0 100644 --- a/chromium/chrome/browser/resources/settings/people_page/user_list.js +++ b/chromium/chrome/browser/resources/settings/people_page/user_list.js @@ -110,7 +110,8 @@ Polymer({ * @private */ getProfilePictureUrl_: function(user) { - return 'chrome://userimage/' + user.email + '?id=' + Date.now(); + return 'chrome://userimage/' + user.email + '?id=' + Date.now() + + '&frame=0'; }, /** 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 3b298a30f59..6ebd338e294 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 @@ -127,20 +127,20 @@ Polymer({ // We're abandoning discovery in favor of manual specification, so // drop the selection if one exists. this.selectedPrinter = getEmptyPrinter_(); - this.$$('add-printer-dialog').close(); + this.close(); this.fire('open-manually-add-printer-dialog'); }, /** @private */ onCancelTap_: function() { this.stopDiscoveringPrinters_(); - this.$$('add-printer-dialog').close(); + this.close(); }, /** @private */ switchToConfiguringDialog_: function() { this.stopDiscoveringPrinters_(); - this.$$('add-printer-dialog').close(); + this.close(); this.fire('open-configuring-printer-dialog'); }, }); @@ -205,14 +205,20 @@ Polymer({ }, }, + close: function() { + this.$$('add-printer-dialog').close(); + }, + /** @private */ onCancelTap_: function() { - this.$$('add-printer-dialog').close(); + this.close(); + settings.CupsPrintersBrowserProxyImpl.getInstance().cancelPrinterSetUp( + this.activePrinter); }, /** @private */ switchToConfiguringDialog_: function() { - this.$$('add-printer-dialog').close(); + this.close(); this.fire('open-configuring-printer-dialog'); }, @@ -244,7 +250,7 @@ Polymer({ /** @private */ onCancelConfiguringTap_: function() { - this.$$('add-printer-dialog').close(); + this.close(); this.fire('configuring-dialog-closed'); }, diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_printers.html b/chromium/chrome/browser/resources/settings/printing_page/cups_printers.html index 36374ba0f2c..3df538af0ea 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_printers.html +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_printers.html @@ -51,8 +51,9 @@ justify-content: space-around; } - #addPrinterErrorMessage a[is='action-link'] { - text-transform: uppercase; + #addPrinterFailedMessage { + padding: 0 15px; + text-align: justify; } </style> @@ -92,10 +93,9 @@ $i18n{printerAddedSuccessfulMessage} </div> <div class="center" id="addPrinterErrorMessage" hidden> - <span>$i18n{printerAddedFailedMessage}</span> - <a is="action-link" on-tap="onAddPrinterTap_"> - $i18n{printerAddedTryAgainMessage} - </a> + <span id="addPrinterFailedMessage"> + $i18n{printerAddedFailedMessage} + </span> </div> </div> </template> diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_printers.js b/chromium/chrome/browser/resources/settings/printing_page/cups_printers.js index b1e36c367b3..0957580006d 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_printers.js +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_printers.js @@ -95,19 +95,53 @@ Polymer({ }, /** - * @param {boolean} success + * @param {PrinterSetupResult} result_code * @param {string} printerName * @private */ - onAddPrinter_: function(success, printerName) { - if (success) { + onAddPrinter_: function(result_code, printerName) { + if (result_code == PrinterSetupResult.SUCCESS) { this.updateCupsPrintersList_(); var message = this.$.addPrinterDoneMessage; message.textContent = loadTimeData.getStringF('printerAddedSuccessfulMessage', printerName); } else { var message = this.$.addPrinterErrorMessage; + var messageText = this.$.addPrinterFailedMessage; + switch (result_code) { + case PrinterSetupResult.FATAL_ERROR: + messageText.textContent = + loadTimeData.getString('printerAddedFatalErrorMessage'); + break; + case PrinterSetupResult.PRINTER_UNREACHABLE: + messageText.textContent = + loadTimeData.getString('printerAddedPrinterUnreachableMessage'); + break; + case PrinterSetupResult.DBUS_ERROR: + // Simply display a generic error message as this error should only + // occur when a call to Dbus fails which isn't meaningful to the user. + messageText.textContent = + loadTimeData.getString('printerAddedFailedmMessage'); + break; + case PrinterSetupResult.PPD_TOO_LARGE: + messageText.textContent = + loadTimeData.getString('printerAddedPpdTooLargeMessage'); + break; + case PrinterSetupResult.INVALID_PPD: + messageText.textContent = + loadTimeData.getString('printerAddedInvalidPpdMessage'); + break; + case PrinterSetupResult.PPD_NOT_FOUND: + messageText.textContent = + loadTimeData.getString('printerAddedPpdNotFoundMessage'); + break; + case PrinterSetupResult.PPD_UNRETRIEVABLE: + messageText.textContent = + loadTimeData.getString('printerAddedPpdUnretrievableMessage'); + break; + } } + message.hidden = false; window.setTimeout(function() { message.hidden = true; diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js b/chromium/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js index 29ad9bdb4b5..6cc0b682015 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js @@ -69,6 +69,22 @@ var PrinterMakeModel; var PrinterPpdMakeModel; /** + * @enum {number} + * These values must be kept in sync with the PrinterSetupResult enum in + * chrome/browser/chromeos/printing/printer_configurer.h. + */ +var PrinterSetupResult = { + FATAL_ERROR: 0, + SUCCESS: 1, + PRINTER_UNREACHABLE: 2, + DBUS_ERROR: 3, + PPD_TOO_LARGE: 10, + INVALID_PPD: 11, + PPD_NOT_FOUND: 12, + PPD_UNRETRIEVABLE: 13, +}; + +/** * @typedef {{ * message: string * }} @@ -135,6 +151,12 @@ cr.define('settings', function() { * @param{string} printerId */ addDiscoveredPrinter(printerId) {} + + /** + * Report to the handler that setup was cancelled. + * @param {!CupsPrinterInfo} newPrinter + */ + cancelPrinterSetUp(newPrinter) {} } /** @@ -200,6 +222,11 @@ cr.define('settings', function() { addDiscoveredPrinter(printerId) { chrome.send('addDiscoveredPrinter', [printerId]); } + + /** @override */ + cancelPrinterSetUp(newPrinter) { + chrome.send('cancelPrinterSetUp', [newPrinter]); + } } cr.addSingletonGetter(CupsPrintersBrowserProxyImpl); 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 9485e7c1659..52fff4a8457 100644 --- a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html +++ b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html @@ -219,7 +219,8 @@ category="{{ContentSettingsTypes.AUTOMATIC_DOWNLOADS}}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.AUTOMATIC_DOWNLOADS}}"> + category="{{ContentSettingsTypes.AUTOMATIC_DOWNLOADS}}" + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -232,7 +233,8 @@ category="{{ContentSettingsTypes.BACKGROUND_SYNC}}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.BACKGROUND_SYNC}}"> + category="{{ContentSettingsTypes.BACKGROUND_SYNC}}" + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -246,7 +248,8 @@ "$i18n{siteSettingsAskBeforeAccessingRecommended}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.CAMERA}}" read-only-list> + category="{{ContentSettingsTypes.CAMERA}}" read-only-list + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -271,7 +274,8 @@ </button> </div> <category-setting-exceptions - category="{{ContentSettingsTypes.COOKIES}}"> + category="{{ContentSettingsTypes.COOKIES}}" + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -291,7 +295,8 @@ toggle-on-label="$i18n{siteSettingsShowAllRecommended}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.IMAGES}}"> + category="{{ContentSettingsTypes.IMAGES}}" + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -303,7 +308,8 @@ category="{{ContentSettingsTypes.GEOLOCATION}}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.GEOLOCATION}}" read-only-list> + category="{{ContentSettingsTypes.GEOLOCATION}}" read-only-list + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -333,7 +339,8 @@ category="{{ContentSettingsTypes.JAVASCRIPT}}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.JAVASCRIPT}}"> + category="{{ContentSettingsTypes.JAVASCRIPT}}" + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -347,7 +354,8 @@ category="{{ContentSettingsTypes.SOUND}}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.SOUND}}"> + category="{{ContentSettingsTypes.SOUND}}" + block-header="$i18n{siteSettingsBlockSound}"> </category-setting-exceptions> </settings-subpage> </template> @@ -362,7 +370,8 @@ "$i18n{siteSettingsAskBeforeAccessingRecommended}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.MIC}}" read-only-list> + category="{{ContentSettingsTypes.MIC}}" read-only-list + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -374,7 +383,8 @@ category="{{ContentSettingsTypes.NOTIFICATIONS}}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.NOTIFICATIONS}}"> + category="{{ContentSettingsTypes.NOTIFICATIONS}}" + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -394,7 +404,8 @@ </a> </if> <category-setting-exceptions - category="{{ContentSettingsTypes.PLUGINS}}"> + category="{{ContentSettingsTypes.PLUGINS}}" + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -405,7 +416,8 @@ toggle-on-label="$i18n{siteSettingsAllowed}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.POPUPS}}"> + category="{{ContentSettingsTypes.POPUPS}}" + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -420,7 +432,8 @@ </category-default-setting> <category-setting-exceptions category="{{ContentSettingsTypes.ADS}}" - read-only-list> + read-only-list + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -434,7 +447,8 @@ category="{{ContentSettingsTypes.UNSANDBOXED_PLUGINS}}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.UNSANDBOXED_PLUGINS}}"> + category="{{ContentSettingsTypes.UNSANDBOXED_PLUGINS}}" + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -447,7 +461,8 @@ category="{{ContentSettingsTypes.MIDI_DEVICES}}"> </category-default-setting> <category-setting-exceptions - category="{{ContentSettingsTypes.MIDI_DEVICES}}" read-only-list> + category="{{ContentSettingsTypes.MIDI_DEVICES}}" read-only-list + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </settings-subpage> </template> @@ -494,7 +509,8 @@ <template is="dom-if" if="[[prefs.settings.privacy.drm_enabled.value]]"> <category-setting-exceptions - category="{{ContentSettingsTypes.PROTECTED_CONTENT}}"> + category="{{ContentSettingsTypes.PROTECTED_CONTENT}}" + block-header="$i18n{siteSettingsBlock}"> </category-setting-exceptions> </template> </if> diff --git a/chromium/chrome/browser/resources/settings/reset_page/powerwash_dialog.js b/chromium/chrome/browser/resources/settings/reset_page/powerwash_dialog.js index ae48f3d09ba..2ff5c06ee71 100644 --- a/chromium/chrome/browser/resources/settings/reset_page/powerwash_dialog.js +++ b/chromium/chrome/browser/resources/settings/reset_page/powerwash_dialog.js @@ -4,12 +4,17 @@ /** * @fileoverview - * 'settings-reset-page' is the settings page containing reset - * settings. + * 'settings-powerwash-dialog' is a dialog shown to request confirmation from + * the user for a device reset (aka powerwash). */ Polymer({ is: 'settings-powerwash-dialog', + properties: { + /** @public */ + requestTpmFirmwareUpdate: Boolean, + }, + /** @override */ attached: function() { settings.ResetBrowserProxyImpl.getInstance().onPowerwashDialogShow(); @@ -23,6 +28,7 @@ Polymer({ /** @private */ onRestartTap_: function() { - settings.LifetimeBrowserProxyImpl.getInstance().factoryReset(); + settings.LifetimeBrowserProxyImpl.getInstance().factoryReset( + this.requestTpmFirmwareUpdate); }, }); diff --git a/chromium/chrome/browser/resources/settings/route.js b/chromium/chrome/browser/resources/settings/route.js index d4dee7d83f3..20b8d4b29a7 100644 --- a/chromium/chrome/browser/resources/settings/route.js +++ b/chromium/chrome/browser/resources/settings/route.js @@ -160,6 +160,15 @@ cr.define('settings', function() { } /** + * Returns the absolute path string for this Route, assuming this function + * has been called from within chrome://settings. + * @return {string} + */ + getAbsolutePath() { + return window.location.origin + this.path; + } + + /** * Returns true if this route matches or is an ancestor of the parameter. * @param {!settings.Route} route * @return {boolean} diff --git a/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js b/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js index cd6e4d6f295..947dcbefc39 100644 --- a/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js @@ -85,6 +85,8 @@ cr.define('settings', function() { /** @param {boolean} enabled */ setHotwordSearchEnabled(enabled) {} + + turnOnGoogleAssistant() {} } /** @@ -140,6 +142,11 @@ cr.define('settings', function() { setHotwordSearchEnabled(enabled) { chrome.send('setHotwordSearchEnabled', [enabled]); } + + /** @override */ + turnOnGoogleAssistant() { + chrome.send('turnOnGoogleAssistant'); + } } // The singleton instance_ is replaced with a test version of this wrapper 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 ad39252a6b1..e10d8141330 100644 --- a/chromium/chrome/browser/resources/settings/search_page/search_page.html +++ b/chromium/chrome/browser/resources/settings/search_page/search_page.html @@ -150,20 +150,29 @@ <if expr="chromeos"> <!-- Google Assistant --> - <template is="dom-if" if="[[showAssistantSection_( - voiceInteractionFeatureEnabled_, prefs.arc.enabled.value, - prefs.arc.voice_interaction_value_prop.accepted.value)]]"> + <template is="dom-if" if="[[voiceInteractionFeatureEnabled_]]"> <div id="assistant-subpage-trigger" class="settings-box two-line" on-tap="onGoogleAssistantTap_" actionable> - <div class="start"> - $i18n{searchGoogleAssistant} - <div class="secondary"> - [[getAssistantEnabledDisabledLabel_( - prefs.settings.voice_interaction.enabled.value)]] + <div class="start"> + $i18n{searchGoogleAssistant} + <div class="secondary"> + [[getAssistantEnabledDisabledLabel_( + prefs.settings.voice_interaction.enabled.value)]] + </div> </div> - </div> - <button class="subpage-arrow" is="paper-icon-button-light" - aria-label="$i18n{searchGoogleAssistant}"></button> + <template is="dom-if" if="[[assistantOn_]]"> + <button class="subpage-arrow" is="paper-icon-button-light" + aria-label="$i18n{searchGoogleAssistant}"></button> + </template> + <template is="dom-if" if="[[!assistantOn_]]"> + <div class="separator"></div> + <paper-button id="enable" class="secondary-button" + on-tap="onAssistantTurnOnTap_" + aria-label="$i18n{searchPageTitle}" + aria-describedby="secondaryText"> + $i18n{assistantTurnOn} + </paper-button> + </template> </div> </template> </if> 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 0d89eee48c0..3dbfcf19d69 100644 --- a/chromium/chrome/browser/resources/settings/search_page/search_page.js +++ b/chromium/chrome/browser/resources/settings/search_page/search_page.js @@ -14,6 +14,12 @@ Polymer({ properties: { prefs: Object, + // <if expr="chromeos"> + arcEnabled: Boolean, + + voiceInteractionValuePropAccepted: Boolean, + // </if> + /** * List of default search engines available. * @private {!Array<!SearchEngine>} @@ -50,6 +56,13 @@ Polymer({ value: function() { return loadTimeData.getBoolean('enableVoiceInteraction'); }, + }, + + /** @private */ + assistantOn_: { + type: Boolean, + computed: + 'isAssistantTurnedOn_(arcEnabled, voiceInteractionValuePropAccepted)', } // </if> }, @@ -112,8 +125,18 @@ Polymer({ /** @private */ onGoogleAssistantTap_: function() { assert(this.voiceInteractionFeatureEnabled_); + + if (!this.assistantOn_) { + return; + } + settings.navigateTo(settings.routes.GOOGLE_ASSISTANT); }, + + /** @private */ + onAssistantTurnOnTap_: function(event) { + this.browserProxy_.turnOnGoogleAssistant(); + }, // </if> /** @@ -190,15 +213,13 @@ Polymer({ 'searchGoogleAssistantDisabled'); }, - /** - * @param {boolean} featureAvailable - * @param {boolean} arcEnabled - * @return {boolean} - * @private + /** @private + * @param {boolean} arcEnabled + * @param {boolean} valuePropAccepted + * @return {boolean} */ - showAssistantSection_: function( - featureAvailable, arcEnabled, valuePropAccepted) { - return featureAvailable && arcEnabled && valuePropAccepted; + isAssistantTurnedOn_: function(arcEnabled, valuePropAccepted) { + return arcEnabled && valuePropAccepted; }, // </if> diff --git a/chromium/chrome/browser/resources/settings/search_settings.js b/chromium/chrome/browser/resources/settings/search_settings.js index 2fb5a818ce4..b05aa1106f4 100644 --- a/chromium/chrome/browser/resources/settings/search_settings.js +++ b/chromium/chrome/browser/resources/settings/search_settings.js @@ -213,13 +213,10 @@ cr.define('settings', function() { // Dynamically position the bubble at the edge the associated control // element. var updatePosition = function() { - if (innards.classList.contains('above')) { - searchBubble.style.top = - element.offsetTop - searchBubble.offsetHeight + 'px'; - } else { - searchBubble.style.top = - element.offsetTop + element.offsetHeight + 'px'; - } + searchBubble.style.top = element.offsetTop + + (innards.classList.contains('above') ? -searchBubble.offsetHeight : + element.offsetHeight) + + 'px'; }; updatePosition(); diff --git a/chromium/chrome/browser/resources/settings/settings.html b/chromium/chrome/browser/resources/settings/settings.html index f56ed01b94b..8732184b614 100644 --- a/chromium/chrome/browser/resources/settings/settings.html +++ b/chromium/chrome/browser/resources/settings/settings.html @@ -3,8 +3,8 @@ <head> <meta charset="utf-8"> <title>$i18n{settings}</title> -<if expr="not use_vulcanize"> - <base href="chrome://$i18n{hostname}"> +<if expr="not optimize_webui"> + <base href="chrome://settings"> </if> <style> html { diff --git a/chromium/chrome/browser/resources/settings/settings_icons_css.html b/chromium/chrome/browser/resources/settings/settings_icons_css.html index 6936eac4e18..8e4bc428719 100644 --- a/chromium/chrome/browser/resources/settings/settings_icons_css.html +++ b/chromium/chrome/browser/resources/settings/settings_icons_css.html @@ -4,12 +4,6 @@ <dom-module id="settings-icons"> <template> <style> - button[is='paper-icon-button-light'].icon-visibility { - background-image: url(./images/settings_icon_visibility.svg); - } - button[is='paper-icon-button-light'].icon-visibility-off { - background-image: url(./images/settings_icon_visibility_off.svg); - } <if expr="chromeos"> button[is='paper-icon-button-light'].icon-add-circle { background-image: url(./images/settings_icon_add_circle.svg); 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 735fdfe0324..ffe06b5ee5a 100644 --- a/chromium/chrome/browser/resources/settings/settings_menu/settings_menu.html +++ b/chromium/chrome/browser/resources/settings/settings_menu/settings_menu.html @@ -73,15 +73,15 @@ -webkit-margin-end: 22px; /* 24px - 2px from margin for outline. */ } - .separator { - /* Per Alan@, this line is different from the other separator lines. */ + #menuSeparator { + /* Per bettes@, this is different from the other separator lines. */ border-bottom: 1px solid rgba(0, 0, 0, 0.08); margin-bottom: 8px; margin-top: 8px; } </style> <iron-selector id="topMenu" selectable="a" attr-for-selected="href" - on-iron-activate="onSelectorActivate_"> + on-iron-activate="onSelectorActivate_" role="navigation"> <if expr="chromeos"> <a href="/internet"> <iron-icon icon="settings:network-wifi"></iron-icon> @@ -137,7 +137,8 @@ </iron-icon></paper-button> <iron-collapse id="advancedSubmenu" opened="[[advancedOpened]]" hidden="[[!pageVisibility.advancedSettings]]"> - <iron-selector id="subMenu" selectable="a" attr-for-selected="href"> + <iron-selector id="subMenu" selectable="a" attr-for-selected="href" + role="navigation"> <if expr="chromeos"> <a href="/dateTime"> <iron-icon icon="settings:access-time"></iron-icon> @@ -167,7 +168,7 @@ </a> <if expr="chromeos"> <a href="/multidevice" hidden="[[!showMultidevice]]"> - <iron-icon icon="settings:sync"></iron-icon> + <iron-icon icon="settings:devices-other"></iron-icon> $i18n{multidevicePageTitle} </a> </if> @@ -187,7 +188,7 @@ </a> </iron-selector> </iron-collapse> - <div class="separator"></div> + <div id="menuSeparator"></div> <a id="about-menu" href="/help">$i18n{aboutPageTitle}</a> </iron-selector> </template> 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 d4dd9b9d3f9..7fca4be7942 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 @@ -150,13 +150,19 @@ Polymer({ if (oldRoute) { if (oldRoute.isSubpage() && newRoute.depth > oldRoute.depth) { - // Slide left for a deeper subpage. - this.$.animatedPages.exitAnimation = 'slide-left-animation'; - this.$.animatedPages.entryAnimation = 'slide-from-right-animation'; + var isRtl = loadTimeData.getString('textdirection') == 'rtl'; + var exit = isRtl ? 'right' : 'left'; + var entry = isRtl ? 'left' : 'right'; + this.$.animatedPages.exitAnimation = 'slide-' + exit + '-animation'; + this.$.animatedPages.entryAnimation = + 'slide-from-' + entry + '-animation'; } else if (oldRoute.depth > newRoute.depth) { - // Slide right for a shallower subpage. - this.$.animatedPages.exitAnimation = 'slide-right-animation'; - this.$.animatedPages.entryAnimation = 'slide-from-left-animation'; + var isRtl = loadTimeData.getString('textdirection') == 'rtl'; + var exit = isRtl ? 'left' : 'right'; + var entry = isRtl ? 'right' : 'left'; + this.$.animatedPages.exitAnimation = 'slide-' + exit + '-animation'; + this.$.animatedPages.entryAnimation = + 'slide-from-' + entry + '-animation'; } else { // The old route is not a subpage or is at the same level, so just fade. this.$.animatedPages.exitAnimation = 'settings-fade-out-animation'; 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 ae615482e8b..cdbdcb2689a 100644 --- a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html +++ b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html @@ -46,10 +46,8 @@ } h1 { - color: var(--settings-nav-grey); flex: 1; /* Push other items to the end. */ - font-size: 107.6923%; /* Go to 14px from 13px */ - font-weight: 500; + @apply(--cr-title-text); } settings-subpage-search { 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 46ede2dd2bc..d00516cda67 100644 --- a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.js +++ b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.js @@ -49,6 +49,31 @@ Polymer({ }, }, + /** @override */ + attached: function() { + if (!!this.searchLabel) { + // |searchLabel| should not change dynamically. + this.listen(this, 'clear-subpage-search', 'onClearSubpageSearch_'); + } + }, + + /** @override */ + detached: function() { + if (!!this.searchLabel) { + // |searchLabel| should not change dynamically. + this.unlisten(this, 'clear-subpage-search', 'onClearSubpageSearch_'); + } + }, + + /** + * Clear the value of the search field. + * @param {!Event} e + */ + onClearSubpageSearch_: function(e) { + e.stopPropagation(); + this.$$('settings-subpage-search').setValue(''); + }, + /** @private */ onTapBack_: function() { settings.navigateToPreviousRoute(); diff --git a/chromium/chrome/browser/resources/settings/settings_resources.grd b/chromium/chrome/browser/resources/settings/settings_resources.grd index de0aeb34fa1..93c5edf1a72 100644 --- a/chromium/chrome/browser/resources/settings/settings_resources.grd +++ b/chromium/chrome/browser/resources/settings/settings_resources.grd @@ -76,7 +76,8 @@ type="chrome_html" /> <structure name="IDR_SETTINGS_ALL_SITES_JS" file="site_settings/all_sites.js" - type="chrome_html" /> + type="chrome_html" + preprocess="true" /> <structure name="IDR_SETTINGS_CATEGORY_DEFAULT_SETTING_HTML" file="site_settings/category_default_setting.html" type="chrome_html" /> @@ -417,9 +418,6 @@ <structure name="IDR_SETTINGS_COOKIE_INFO_JS" file="site_settings/cookie_info.js" type="chrome_html" /> - <structure name="IDR_SETTINGS_COOKIE_TREE_NODE_JS" - file="site_settings/cookie_tree_node.js" - type="chrome_html" /> <if expr="not chromeos"> <structure name="IDR_SETTINGS_DEFAULT_BROWSER_BROWSER_PROXY_HTML" file="default_browser_page/default_browser_browser_proxy.html" @@ -523,12 +521,6 @@ type="chrome_html" /> </if> - <structure name="IDR_SETTINGS_DIRECTION_DELEGATE_HTML" - file="direction_delegate.html" - type="chrome_html" /> - <structure name="IDR_SETTINGS_DIRECTION_DELEGATE_JS" - file="direction_delegate.js" - type="chrome_html" /> <structure name="IDR_SETTINGS_DOWNLOADS_BROWSER_PROXY_HTML" file="downloads_page/downloads_browser_proxy.html" type="chrome_html" /> @@ -681,7 +673,8 @@ type="chrome_html" /> <structure name="IDR_SETTINGS_PASSWORDS_SECTION_JS" file="passwords_and_forms_page/passwords_section.js" - type="chrome_html" /> + type="chrome_html" + preprocess="true" /> <structure name="IDR_SETTINGS_PASSWORD_EDIT_DIALOG_HTML" file="passwords_and_forms_page/password_edit_dialog.html" type="chrome_html" /> @@ -826,6 +819,12 @@ <structure name="IDR_SETTINGS_CLOUD_PRINTING_PAGE_JS" file="printing_page/cloud_printers.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_LOCAL_DATA_BROWSER_PROXY_HTML" + file="site_settings/local_data_browser_proxy.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_LOCAL_DATA_BROWSER_PROXY_JS" + file="site_settings/local_data_browser_proxy.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_PAGE_VISIBILITY_HTML" file="page_visibility.html" type="chrome_html" /> @@ -868,12 +867,6 @@ file="route.js" type="chrome_html" preprocess="true" /> - <structure name="IDR_SETTINGS_COOKIE_TREE_BEHAVIOR_HTML" - file="site_settings/cookie_tree_behavior.html" - type="chrome_html" /> - <structure name="IDR_SETTINGS_COOKIE_TREE_BEHAVIOR_JS" - file="site_settings/cookie_tree_behavior.js" - type="chrome_html" /> <structure name="IDR_SETTINGS_SITE_DATA_HTML" file="site_settings/site_data.html" type="chrome_html" /> @@ -900,8 +893,7 @@ type="chrome_html" /> <structure name="IDR_SETTINGS_CONSTANTS_JS" file="site_settings/constants.js" - type="chrome_html" - preprocess="true" /> + type="chrome_html" /> <structure name="IDR_SETTINGS_CONSTANTS_HTML" file="site_settings/constants.html" type="chrome_html" /> @@ -910,7 +902,8 @@ type="chrome_html" /> <structure name="IDR_SETTINGS_SITE_SETTINGS_PAGE_JS" file="site_settings_page/site_settings_page.js" - type="chrome_html" /> + type="chrome_html" + preprocess="true" /> <structure name="IDR_SETTINGS_SITE_SETTINGS_PREFS_BROWSER_PROXY_HTML" file="site_settings/site_settings_prefs_browser_proxy.html" type="chrome_html" /> @@ -919,16 +912,19 @@ type="chrome_html" /> <structure name="IDR_SETTINGS_SITE_DETAILS_HTML" file="site_settings/site_details.html" - type="chrome_html" /> + type="chrome_html" + preprocess="true" /> <structure name="IDR_SETTINGS_SITE_DETAILS_JS" file="site_settings/site_details.js" - type="chrome_html" /> + type="chrome_html" + preprocess="true" /> <structure name="IDR_SETTINGS_SITE_DETAILS_PERMISSION_HTML" file="site_settings/site_details_permission.html" type="chrome_html" /> <structure name="IDR_SETTINGS_SITE_DETAILS_PERMISSION_JS" file="site_settings/site_details_permission.js" - type="chrome_html" /> + type="chrome_html" + preprocess="true" /> <structure name="IDR_SETTINGS_SEARCH_ENGINES_PAGE_SEARCH_ENGINE_DIALOG_JS" file="search_engines_page/search_engine_dialog.js" type="chrome_html" /> @@ -1148,18 +1144,6 @@ <structure name="IDR_SETTINGS_MULTIDEVICE_PAGE_JS" file="multidevice_page/multidevice_page.js" type="chrome_html" /> - <structure name="IDR_SETTINGS_NETWORK_CONFIG_INPUT_HTML" - file="internet_page/network_config_input.html" - type="chrome_html" /> - <structure name="IDR_SETTINGS_NETWORK_CONFIG_INPUT_JS" - file="internet_page/network_config_input.js" - type="chrome_html" /> - <structure name="IDR_SETTINGS_NETWORK_CONFIG_SELECT_HTML" - file="internet_page/network_config_select.html" - type="chrome_html" /> - <structure name="IDR_SETTINGS_NETWORK_CONFIG_SELECT_JS" - file="internet_page/network_config_select.js" - type="chrome_html" /> <structure name="IDR_SETTINGS_NETWORK_PROXY_SECTION_HTML" file="internet_page/network_proxy_section.html" type="chrome_html" /> diff --git a/chromium/chrome/browser/resources/settings/settings_shared_css.html b/chromium/chrome/browser/resources/settings/settings_shared_css.html index 4f0c690a92b..36f43725cd7 100644 --- a/chromium/chrome/browser/resources/settings/settings_shared_css.html +++ b/chromium/chrome/browser/resources/settings/settings_shared_css.html @@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/paper_checkbox_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_input_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/paper_toggle_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> @@ -10,7 +11,7 @@ <!-- Common styles for Material Design settings. --> <dom-module id="settings-shared"> <template> - <style include="settings-icons paper-button-style paper-checkbox-style paper-toggle-style cr-shared-style"> + <style include="settings-icons paper-button-style paper-checkbox-style paper-input-style paper-toggle-style cr-shared-style"> /* Prevent action-links from being selected to avoid accidental * selection when trying to click it. */ a[is=action-link] { @@ -62,6 +63,11 @@ -webkit-margin-start: var(--cr-icon-ripple-margin); } + neon-animatable { + display: flex; + flex-direction: column; + } + /* For "Advanced" toggle button. */ paper-button[toggles][active] { background-color: var(--paper-grey-300); @@ -84,7 +90,7 @@ } /* Adjust the margin between the separator and the first button. */ - .settings-box .separator + paper-button { + .separator + paper-button { -webkit-margin-start: calc(var(--cr-button-edge-spacing) * -1); } @@ -111,9 +117,6 @@ a[href] { color: var(--google-blue-700); - } - - a[href] { text-decoration: none; } @@ -131,11 +134,6 @@ color: var(--google-blue-500); } - paper-input { - /* Fix issue with focus animation making labels wiggle. */ - transform: translate3d(0, 0, 0); - } - controlled-radio-button, paper-radio-button { --paper-radio-button-checked-color: var(--google-blue-500); @@ -168,10 +166,6 @@ min-width: 0; } - .button-strip { - text-align: end; - } - .header-aligned-button { margin-top: 12px; /* Align paper-button with <h2>. */ } @@ -318,11 +312,6 @@ min-width: 0; /* Workaround for text elision in sub-elements. */ } - /* Expand to fill-up available space. */ - .settings-row.wide { - flex: 1; - } - .no-outline { background: none; outline: none; @@ -347,7 +336,7 @@ /* The separator a vertical line like a horizontal rule <hr> tag, but goes * the other way. An example is near the |sign out| button on the People * settings. */ - :-webkit-any(.settings-box, .list-item) .separator { + .separator { -webkit-border-start: var(--settings-separator-line); -webkit-margin-end: var(--settings-box-row-padding); -webkit-margin-start: var(--settings-box-row-padding); @@ -357,7 +346,7 @@ var(--settings-separator-gaps)); } - :-webkit-any(.settings-box, .list-item).two-line .separator { + .two-line .separator { height: calc(var(--settings-row-two-line-min-height) - 2 * var(--settings-separator-gaps)); } diff --git a/chromium/chrome/browser/resources/settings/settings_ui/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/settings_ui/compiled_resources2.gyp index 4000384e577..57468081491 100644 --- a/chromium/chrome/browser/resources/settings/settings_ui/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/settings_ui/compiled_resources2.gyp @@ -7,11 +7,11 @@ 'target_name': 'settings_ui', 'dependencies': [ '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', + '<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:cr_container_shadow_behavior', '<(DEPTH)/ui/webui/resources/cr_elements/cr_drawer/compiled_resources2.gyp:cr_drawer', '<(DEPTH)/ui/webui/resources/cr_elements/cr_toolbar/compiled_resources2.gyp:cr_toolbar', '<(DEPTH)/ui/webui/resources/cr_elements/cr_toolbar/compiled_resources2.gyp:cr_toolbar_search_field', '<(DEPTH)/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp:cr_policy_indicator_behavior', - '../compiled_resources2.gyp:direction_delegate', '../compiled_resources2.gyp:global_scroll_target_behavior', '../prefs/compiled_resources2.gyp:prefs', '../settings_main/compiled_resources2.gyp:settings_main', diff --git a/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html index 27bc50b5909..0b217e7ada0 100644 --- a/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html +++ b/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html @@ -1,10 +1,10 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html"> <link rel="import" href="chrome://resources/cr_elements/cr_drawer/cr_drawer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> -<link rel="import" href="../direction_delegate.html"> <link rel="import" href="../global_scroll_target_behavior.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="../icons.html"> @@ -33,12 +33,6 @@ user-select: text; } - .last { - display: flex; - justify-content: flex-end; - width: 100%; - } - dialog[is='cr-drawer'] { z-index: 2; } @@ -58,25 +52,6 @@ overflow: overlay; position: relative; } - - #dropShadow { - /* TODO(dpapad): This box-shadow is repeated in Settings, History and - Downloads. Define a CSS variable instead and re-use. */ - box-shadow: inset 0 5px 6px -3px rgba(0, 0, 0, 0.4); - height: 6px; - left: 0; - opacity: 0; - pointer-events: none; - position: absolute; - right: 0; - top: 56px; - transition: opacity 500ms; - z-index: 1; - } - - #dropShadow.has-shadow { - opacity: 1; - } </style> <settings-prefs id="prefs" prefs="{{prefs}}"></settings-prefs> <cr-toolbar page-name="$i18n{settings}" @@ -86,11 +61,11 @@ spinner-active="[[toolbarSpinnerActive_]]" menu-label="$i18n{menuButtonLabel}" on-search-changed="onSearchChanged_" - role="none" + role="banner" show-menu> </cr-toolbar> <dialog id="drawer" is="cr-drawer" on-close="onMenuClosed_" - heading="$i18n{settings}"> + heading="$i18n{settings}" align="$i18n{textdirection}"> <div class="drawer-content"> <template is="dom-if" id="drawerTemplate"> <settings-menu page-visibility="[[pageVisibility_]]" @@ -103,10 +78,7 @@ </template> </div> </dialog> - <div id="dropShadow"></div> <div id="container" class="no-outline"> - <!-- Used by IntersectionObserver, has a 0px height intentionally --> - <div id="intersectionProbe"></div> <settings-main id="main" prefs="{{prefs}}" toolbar-spinner-active="{{toolbarSpinnerActive_}}" page-visibility="[[pageVisibility_]]" 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 cb96a4ffd15..9a00a856991 100644 --- a/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js +++ b/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js @@ -20,7 +20,7 @@ settings.defaultResourceLoaded = true; Polymer({ is: 'settings-ui', - behaviors: [settings.RouteObserverBehavior], + behaviors: [settings.RouteObserverBehavior, CrContainerShadowBehavior], properties: { /** @@ -28,13 +28,6 @@ Polymer({ */ prefs: Object, - /** @type {?settings.DirectionDelegate} */ - directionDelegate: { - observer: 'directionDelegateChanged_', - type: Object, - value: new settings.DirectionDelegateImpl(), - }, - /** @private */ advancedOpened_: { type: Boolean, @@ -127,13 +120,15 @@ Polymer({ loadTimeData.getString('networkListItemInitializing'), networkListItemNotConnected: loadTimeData.getString('networkListItemNotConnected'), + networkListItemNoNetwork: + loadTimeData.getString('networkListItemNoNetwork'), vpnNameTemplate: loadTimeData.getString('vpnNameTemplate'), }; // </if> this.showAndroidApps_ = loadTimeData.valueExists('androidAppsVisible') && loadTimeData.getBoolean('androidAppsVisible'); - this.showMultidevice_ = + this.showMultidevice_ = this.showAndroidApps_ && loadTimeData.valueExists('enableMultideviceSettings') && loadTimeData.getBoolean('enableMultideviceSettings'); this.havePlayStoreApp_ = loadTimeData.valueExists('havePlayStoreApp') && @@ -148,9 +143,6 @@ Polymer({ }); }, - /** @private {?IntersectionObserver} */ - intersectionObserver_: null, - /** @override */ attached: function() { document.documentElement.classList.remove('loading'); @@ -164,27 +156,11 @@ Polymer({ // Preload bold Roboto so it doesn't load and flicker the first time used. document.fonts.load('bold 12px Roboto'); settings.setGlobalScrollTarget(this.$.container); - - // Setup drop shadow logic. - var callback = entries => { - this.$.dropShadow.classList.toggle( - 'has-shadow', entries[entries.length - 1].intersectionRatio == 0); - }; - - this.intersectionObserver_ = new IntersectionObserver( - callback, - /** @type {IntersectionObserverInit} */ ({ - root: this.$.container, - threshold: 0, - })); - this.intersectionObserver_.observe(this.$.intersectionProbe); }, /** @override */ detached: function() { settings.resetRouteForTesting(); - this.intersectionObserver_.disconnect(); - this.intersectionObserver_ = null; }, /** @param {!settings.Route} route */ @@ -265,9 +241,4 @@ Polymer({ this.$.container.removeAttribute('tabindex'); }); }, - - /** @private */ - directionDelegateChanged_: function() { - this.$.drawer.align = this.directionDelegate.isRtl() ? 'right' : 'left'; - }, }); diff --git a/chromium/chrome/browser/resources/settings/settings_vars_css.html b/chromium/chrome/browser/resources/settings/settings_vars_css.html index 9efc68e9f65..595e689d955 100644 --- a/chromium/chrome/browser/resources/settings/settings_vars_css.html +++ b/chromium/chrome/browser/resources/settings/settings_vars_css.html @@ -59,38 +59,11 @@ --iron-icon-height: var(--cr-icon-size); --iron-icon-width: var(--cr-icon-size); - --paper-input-container-focus-color: var(--google-blue-500); - --paper-input-container-input: { - color: inherit; - font-size: inherit; - font-weight: inherit; - line-height: 1.54; - vertical-align: baseline; - }; - --paper-input-container-label: { - font-size: inherit; - line-height: 1.54; - }; - --paper-input-container-label-floating: { - /* Using "rem" to make it easier to equalize with other labels. */ - font-size: 1rem; - line-height: 1.65rem; - }; - --paper-input-error: { - font-size: 92.31%; /* Should be 12px when 100% is 13px. */ - line-height: 1.54; - overflow: visible; /* Half-visible error message is not useful at all. */ - }; - --paper-input-max-width: 264px; --paper-radio-button-ink-size: 40px; --paper-radio-button-label-color: inherit; --paper-radio-button-size: 16px; --paper-radio-group-item-padding: 0; - --settings-input-underline: { - border-color: var(--paper-grey-300); - }; - - --paper-input-container-underline: var(--settings-input-underline); + --paper-tabs-selection-bar-color: var(--paper-blue-500); } </style> 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 3ac0f36bbc7..23f37fd1441 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/all_sites.js +++ b/chromium/chrome/browser/resources/settings/site_settings/all_sites.js @@ -46,6 +46,10 @@ Polymer({ var types = Object.values(settings.ContentSettingsTypes); for (var i = 0; i < types.length; i++) { var type = types[i]; + // <if expr="not chromeos"> + if (type == settings.ContentSettingsTypes.PROTECTED_CONTENT) + continue; + // </if> if (type == settings.ContentSettingsTypes.PROTOCOL_HANDLERS || type == settings.ContentSettingsTypes.USB_DEVICES || type == settings.ContentSettingsTypes.ZOOM_LEVELS) { diff --git a/chromium/chrome/browser/resources/settings/site_settings/category_default_setting.js b/chromium/chrome/browser/resources/settings/site_settings/category_default_setting.js index 316f8bbed74..08f2e59a2fa 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/category_default_setting.js +++ b/chromium/chrome/browser/resources/settings/site_settings/category_default_setting.js @@ -82,9 +82,15 @@ Polymer({ /** * A handler for changing the default permission value for a content type. + * This is also called during page setup after we get the default state. * @private */ onChangePermissionControl_: function() { + // Don't override user settings with enforced settings. + if (this.controlParams_.enforcement == + chrome.settingsPrivate.Enforcement.ENFORCED) { + return; + } switch (this.category) { case settings.ContentSettingsTypes.ADS: case settings.ContentSettingsTypes.BACKGROUND_SYNC: diff --git a/chromium/chrome/browser/resources/settings/site_settings/category_setting_exceptions.html b/chromium/chrome/browser/resources/settings/site_settings/category_setting_exceptions.html index ab0925dea92..091cc75e231 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/category_setting_exceptions.html +++ b/chromium/chrome/browser/resources/settings/site_settings/category_setting_exceptions.html @@ -18,7 +18,7 @@ <site-list category="[[category]]" category-subtype="[[ContentSetting.BLOCK]]" - category-header="$i18n{siteSettingsBlock}" + category-header="[[blockHeader]]" read-only-list="[[readOnlyList]]"> </site-list> <site-list diff --git a/chromium/chrome/browser/resources/settings/site_settings/category_setting_exceptions.js b/chromium/chrome/browser/resources/settings/site_settings/category_setting_exceptions.js index 0d4a2107300..3bfe4e986d8 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/category_setting_exceptions.js +++ b/chromium/chrome/browser/resources/settings/site_settings/category_setting_exceptions.js @@ -20,6 +20,11 @@ Polymer({ type: Boolean, value: false, }, + + /** + * The heading text for the blocked exception list. + */ + blockHeader: String, }, /** @override */ diff --git a/chromium/chrome/browser/resources/settings/site_settings/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/site_settings/compiled_resources2.gyp index 8130e09241a..fbc1a37e421 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/site_settings/compiled_resources2.gyp @@ -57,36 +57,28 @@ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'media_picker', + 'target_name': 'local_data_browser_proxy', 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', - 'site_settings_behavior', - ], - 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { - 'target_name': 'cookie_info', - 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(EXTERNS_GYP):chrome_send', + '<(EXTERNS_GYP):settings_private', + 'constants', + 'cookie_info', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'cookie_tree_behavior', + 'target_name': 'media_picker', 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', - 'cookie_info', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', 'site_settings_behavior', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'cookie_tree_node', + 'target_name': 'cookie_info', 'dependencies': [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', - 'cookie_info', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -112,11 +104,15 @@ 'target_name': 'site_data', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:focus_without_ink', + '../compiled_resources2.gyp:global_scroll_target_behavior', '../settings_page/compiled_resources2.gyp:settings_subpage_search', - 'cookie_tree_behavior', - 'cookie_tree_node', + 'cookie_info', + 'local_data_browser_proxy', + 'site_settings_behavior', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -126,7 +122,7 @@ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', '../compiled_resources2.gyp:route', - 'site_settings_prefs_browser_proxy', + 'local_data_browser_proxy', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -150,6 +146,7 @@ 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', 'constants', @@ -197,7 +194,6 @@ '<(EXTERNS_GYP):chrome_send', '<(EXTERNS_GYP):settings_private', 'constants', - 'cookie_tree_node', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, diff --git a/chromium/chrome/browser/resources/settings/site_settings/constants.js b/chromium/chrome/browser/resources/settings/site_settings/constants.js index 163fc33fca4..44fbc1546d1 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/constants.js +++ b/chromium/chrome/browser/resources/settings/site_settings/constants.js @@ -7,9 +7,9 @@ cr.exportPath('settings'); /** * All possible contentSettingsTypes that we currently support configuring in * the UI. Both top-level categories and content settings that represent - * individual permissions under Site Details should appear here. This is a - * subset of the constants found in site_settings_helper.cc and the values - * should be kept in sync. + * individual permissions under Site Details should appear here. + * This should be kept in sync with the |kContentSettingsTypeGroupNames| array + * in chrome/browser/ui/webui/site_settings_helper.cc * @enum {string} */ settings.ContentSettingsTypes = { @@ -30,9 +30,7 @@ settings.ContentSettingsTypes = { MIDI_DEVICES: 'midi-sysex', USB_DEVICES: 'usb-chooser-data', ZOOM_LEVELS: 'zoom-levels', - // <if expr="chromeos"> PROTECTED_CONTENT: 'protectedContent', - // </if> ADS: 'ads', }; @@ -58,13 +56,16 @@ settings.ContentSetting = { * @enum {string} */ settings.SiteSettingSource = { + DEFAULT: 'default', + // This source is for the Protected Media Identifier / Protected Content + // content setting only, which is only available on ChromeOS. + DRM_DISABLED: 'drm-disabled', EMBARGO: 'embargo', EXTENSION: 'extension', INSECURE_ORIGIN: 'insecure-origin', KILL_SWITCH: 'kill-switch', POLICY: 'policy', PREFERENCE: 'preference', - DEFAULT: 'default', }; /** diff --git a/chromium/chrome/browser/resources/settings/site_settings/cookie_tree_behavior.html b/chromium/chrome/browser/resources/settings/site_settings/cookie_tree_behavior.html deleted file mode 100644 index a9d7fd711e4..00000000000 --- a/chromium/chrome/browser/resources/settings/site_settings/cookie_tree_behavior.html +++ /dev/null @@ -1,3 +0,0 @@ -<link rel="import" href="site_settings_behavior.html"> -<link rel="import" href="site_settings_prefs_browser_proxy.html"> -<script src="cookie_tree_behavior.js"></script> diff --git a/chromium/chrome/browser/resources/settings/site_settings/cookie_tree_behavior.js b/chromium/chrome/browser/resources/settings/site_settings/cookie_tree_behavior.js deleted file mode 100644 index 38d95db2506..00000000000 --- a/chromium/chrome/browser/resources/settings/site_settings/cookie_tree_behavior.js +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2015 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 behavior for managing a tree of cookies. - */ - -/** @polymerBehavior */ -var CookieTreeBehaviorImpl = { - properties: { - /** - * A summary list of all sites and how many entities each contain. - * @type {!Array<!CookieDataSummaryItem>} - */ - sites: Array, - - /** - * The cookie tree with the details needed to display individual sites and - * their contained data. - * @type {!settings.CookieTreeNode} - * @protected - */ - rootCookieNode: Object, - }, - - /** @override */ - ready: function() { - cr.addWebUIListener( - 'onTreeItemRemoved', this.onTreeItemRemoved_.bind(this)); - this.rootCookieNode = new settings.CookieTreeNode(null); - }, - - /** - * Called when the cookie list is ready to be shown. - * @param {!CookieList} list The cookie list to show. - * @return {Promise} - * @private - */ - loadChildren_: function(list) { - var loadChildrenRecurse = childList => { - var parentId = childList.id; - var children = childList.children; - var prefix = ''; - if (parentId !== null) { - this.rootCookieNode.populateChildNodes( - parentId, this.rootCookieNode, children); - prefix = parentId + ', '; - } - var promises = []; - for (var i = 0; i < children.length; i++) { - var child = children[i]; - if (child.hasChildren) { - promises.push(this.browserProxy.loadCookieChildren(prefix + child.id) - .then(loadChildrenRecurse.bind(this))); - } - } - return Promise.all(promises); - }; - - // New root being added, clear the list and add the nodes. - this.sites = []; - this.rootCookieNode.addChildNodes(this.rootCookieNode, list.children); - return loadChildrenRecurse(list).then(() => { - this.sites = this.rootCookieNode.getSummaryList(); - return Promise.resolve(); - }); - }, - - /** - * Loads (or reloads) the whole cookie list. - * @return {Promise} - */ - loadCookies: function() { - return this.browserProxy.reloadCookies().then( - this.loadChildren_.bind(this)); - }, - - /** - * Called when a single item has been removed (not during delete all). - * @param {!CookieRemovePacket} args The details about what to remove. - * @private - */ - onTreeItemRemoved_: function(args) { - this.rootCookieNode.removeByParentId(args.id, args.start, args.count); - this.sites = this.rootCookieNode.getSummaryList(); - }, - - /** - * Deletes site data for multiple sites. - * @return {Promise} - */ - removeAllCookies: function() { - return this.browserProxy.removeAllCookies().then( - this.loadChildren_.bind(this)); - }, -}; - -/** @polymerBehavior */ -var CookieTreeBehavior = [SiteSettingsBehavior, CookieTreeBehaviorImpl]; diff --git a/chromium/chrome/browser/resources/settings/site_settings/cookie_tree_node.js b/chromium/chrome/browser/resources/settings/site_settings/cookie_tree_node.js deleted file mode 100644 index 2e27145a167..00000000000 --- a/chromium/chrome/browser/resources/settings/site_settings/cookie_tree_node.js +++ /dev/null @@ -1,234 +0,0 @@ -// 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. - -/** - * @typedef {{title: string, - * id: string, - * data: CookieDetails}} - */ -var CookieDataItem; - -/** - * @typedef {{site: string, - * id: string, - * localData: string}} - */ -var CookieDataSummaryItem; - -/** - * @typedef {{id: string, - * start: number, - * children: !Array<CookieDetails>}} - */ -var CookieList; - -/** - * @typedef {{id: string, - * start: number, - * count: number}} - */ -var CookieRemovePacket; - -var categoryLabels = { - 'app_cache': loadTimeData.getString('cookieAppCache'), - 'cache_storage': loadTimeData.getString('cookieCacheStorage'), - 'channel_id': loadTimeData.getString('cookieChannelId'), - 'cookie': loadTimeData.getString('cookieSingular'), - 'database': loadTimeData.getString('cookieDatabaseStorage'), - 'file_system': loadTimeData.getString('cookieFileSystem'), - 'flash_lso': loadTimeData.getString('cookieFlashLso'), - 'indexed_db': loadTimeData.getString('cookieDatabaseStorage'), - 'local_storage': loadTimeData.getString('cookieLocalStorage'), - 'service_worker': loadTimeData.getString('cookieServiceWorker'), - 'media_license': loadTimeData.getString('cookieMediaLicense'), -}; - -/** - * Retrieves the human friendly text to show for the type of cookie. - * @param {string} dataType The datatype to look up. - * @param {string} totalUsage How much data is being consumed. - * @return {string} The human-friendly description for this cookie. - */ -function getCookieDataCategoryText(dataType, totalUsage) { - if (dataType == 'quota') - return totalUsage; - return categoryLabels[dataType]; -} - -cr.define('settings', function() { - 'use strict'; - - class CookieTreeNode { - constructor(data) { - /** - * The data for this cookie node. - * @type {CookieDetails} - */ - this.data = data; - - /** - * The child cookie nodes. - * @private {!Array<!settings.CookieTreeNode>} - */ - this.children_ = []; - } - - /** - * Converts a list of cookies and add them as CookieTreeNode children to - * the given parent node. - * @param {!settings.CookieTreeNode} parentNode The parent node to add - * children to. - * @param {!Array<!CookieDetails>} newNodes The list containing the data to - * add. - */ - addChildNodes(parentNode, newNodes) { - var nodes = newNodes.map(function(x) { - return new settings.CookieTreeNode(x); - }); - parentNode.children_ = nodes; - } - - /** - * Looks up a parent node and adds a list of CookieTreeNodes to them. - * @param {string} parentId The ID of the parent to add the nodes to. - * @param {!settings.CookieTreeNode} startingNode The node to start with - * when looking for the parent node to add the children to. - * @param {!Array<!CookieDetails>} newNodes The list containing the data to - add. - * @return {boolean} True if the parent node was found. - */ - populateChildNodes(parentId, startingNode, newNodes) { - for (var i = 0; i < startingNode.children_.length; ++i) { - if (startingNode.children_[i].data.id == parentId) { - this.addChildNodes(startingNode.children_[i], newNodes); - return true; - } - - if (this.populateChildNodes( - parentId, startingNode.children_[i], newNodes)) { - return true; - } - } - return false; - } - - /** - * Removes child nodes from a node with a given id. - * @param {string} id The id of the parent node to delete from. - * @param {number} firstChild The index of the first child to start deleting - * from. - * @param {number} count The number of children to delete. - */ - removeByParentId(id, firstChild, count) { - var node = id == null ? this : this.fetchNodeById(id, true); - node.children_.splice(firstChild, count); - } - - /** - * Returns an array of cookies from the current node within the cookie tree. - * @return {!Array<!CookieDataItem>} The Cookie List. - */ - getCookieList() { - var list = []; - for (var i = 0; i < this.children_.length; i++) { - var child = this.children_[i]; - for (var j = 0; j < child.children_.length; j++) { - var cookie = child.children_[j]; - list.push({ - title: cookie.data.title, - id: cookie.data.id, - data: cookie.data - }); - } - } - - return list; - } - - /** - * Get a summary list of all sites and their stored data. - * @return {!Array<!CookieDataSummaryItem>} The summary list. - */ - getSummaryList() { - var list = []; - for (var i = 0; i < this.children_.length; ++i) { - var siteEntry = this.children_[i]; - var title = siteEntry.data.title; - var id = siteEntry.data.id; - var description = ''; - - if (siteEntry.children_.length == 0) - continue; - - for (var j = 0; j < siteEntry.children_.length; ++j) { - var descriptionNode = siteEntry.children_[j]; - if (j > 0) - description += ', '; - - // Some types, like quota, have no description nodes. - var dataType = ''; - if (descriptionNode.data.type != undefined) { - dataType = descriptionNode.data.type; - } else { - // A description node might not have children when it's deleted. - if (descriptionNode.children_.length > 0) - dataType = descriptionNode.children_[0].data.type; - } - - var count = - (dataType == 'cookie') ? descriptionNode.children_.length : 0; - if (count > 1) { - description += loadTimeData.getStringF('cookiePlural', count); - } else { - description += getCookieDataCategoryText( - dataType, descriptionNode.data.totalUsage); - } - } - list.push({site: title, id: id, localData: description}); - } - list.sort(function(a, b) { - return a.site.localeCompare(b.site); - }); - return list; - } - - /** - * Fetch a CookieTreeNode by ID. - * @param {string} id The ID to look up. - * @param {boolean} recursive Whether to search the children also. - * @return {settings.CookieTreeNode} The node found, if any. - */ - fetchNodeById(id, recursive) { - for (var i = 0; i < this.children_.length; ++i) { - if (this.children_[i] == null) - return null; - if (this.children_[i].data.id == id) - return this.children_[i]; - if (recursive) { - var node = this.children_[i].fetchNodeById(id, true); - if (node != null) - return node; - } - } - return null; - } - - /** - * Fetch a CookieTreeNode by site. - * @param {string} site The web site to look up. - * @return {?settings.CookieTreeNode} The node found, if any. - */ - fetchNodeBySite(site) { - for (var i = 0; i < this.children_.length; ++i) { - if (this.children_[i].data.title == site) - return this.children_[i]; - } - return null; - } - } - - return { - CookieTreeNode: CookieTreeNode, - }; -}); diff --git a/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.html b/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.html new file mode 100644 index 00000000000..00f30d023ca --- /dev/null +++ b/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.html @@ -0,0 +1 @@ +<script src="local_data_browser_proxy.js"></script> 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 new file mode 100644 index 00000000000..28d4a1dccce --- /dev/null +++ b/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js @@ -0,0 +1,140 @@ +// 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. + +/** + * @fileoverview A helper object used from the Cookies and Local Storage Data + * section. + */ + +/** + * @typedef {{ + * id: string, + * start: number, + * children: !Array<CookieDetails>, + * }} + */ +var CookieList; + +/** + * @typedef {{ + * data: !Object, + * id: string, + * }} + */ +var LocalDataItem; + +/** + * TODO(dschuyler): add |filter| and |order|. + * @typedef {{ + * items: !Array<!LocalDataItem>, + * start: number, + * total: number, + * }} + */ +var LocalDataList; + +cr.define('settings', function() { + /** @interface */ + class LocalDataBrowserProxy { + /** + * @param {string} filter Search filter (use "" for none). + * @param {number} begin Which element to start with. (Similar to 'offset' + * in SQL). The first item is at 0. + * @param {number} count How many list elements are displayed. (Similar to + * 'limit' in SQL). Pass -1 to get all remaining items. + * @return {!Promise<!LocalDataList>} + */ + getDisplayList(filter, begin, count) {} + + /** + * Removes all local data (local storage, cookies, etc.). + * Note: on-tree-item-removed will not be sent. + * @return {!Promise} To signal completion. + */ + removeAll() {} + + /** + * Remove items that pass the current filter. Completion signaled by + * on-tree-item-removed. + */ + removeShownItems() {} + + /** + * Remove a specific list item. Completion signaled by on-tree-item-removed. + * @param {string} id Which element to delete. + */ + removeItem(id) {} + + /** + * Gets the cookie details for a particular site. + * @param {string} site The name of the site. + * @return {!Promise<!CookieList>} + */ + getCookieDetails(site) {} + + /** + * Reloads all local data. + * TODO(dschuyler): rename function to reload(). + * @return {!Promise} To signal completion. + */ + reloadCookies() {} + + /** + * TODO(dschuyler): merge with removeItem(). + * Removes a given cookie. + * @param {string} path The path to the parent cookie. + */ + removeCookie(path) {} + } + + /** + * @implements {settings.LocalDataBrowserProxy} + */ + class LocalDataBrowserProxyImpl { + /** @override */ + getDisplayList(filter, begin, count) { + return cr.sendWithPromise( + 'localData.getDisplayList', filter, begin, count); + } + + /** @override */ + removeAll() { + return cr.sendWithPromise('localData.removeAll'); + } + + /** @override */ + removeShownItems() { + chrome.send('localData.removeShownItems'); + } + + /** @override */ + removeItem(id) { + chrome.send('localData.removeItem', [id]); + } + + /** @override */ + getCookieDetails(site) { + return cr.sendWithPromise('localData.getCookieDetails', site); + } + + /** @override */ + reloadCookies() { + return cr.sendWithPromise('localData.reload'); + } + + /** @override */ + removeCookie(path) { + chrome.send('localData.removeCookie', [path]); + } + } + + // The singleton instance_ is replaced with a test version of this wrapper + // during testing. + cr.addSingletonGetter(LocalDataBrowserProxyImpl); + + return { + LocalDataBrowserProxy: LocalDataBrowserProxy, + LocalDataBrowserProxyImpl: LocalDataBrowserProxyImpl, + }; +}); 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 eb8408f3dc5..eb7d2da4117 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_data.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_data.html @@ -3,13 +3,16 @@ <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/html/web_ui_listener_behavior.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/iron-icon/iron-icon.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-icon-button/paper-icon-button-light.html"> +<link rel="import" href="../global_scroll_target_behavior.html"> <link rel="import" href="../settings_page/settings_subpage_search.html"> <link rel="import" href="../settings_shared_css.html"> -<link rel="import" href="cookie_tree_behavior.html"> +<link rel="import" href="local_data_browser_proxy.html"> <link rel="import" href="site_settings_behavior.html"> <dom-module id="site-data"> @@ -23,38 +26,38 @@ -webkit-margin-start: auto; } - #removeSecondary { + .separator { -webkit-padding-start: 0; } </style> <div class="settings-box continuation"> <paper-button class="secondary-button" id="removeShowingSites" - on-tap="onRemoveShowingSitesTap_" - hidden$="[[!isRemoveButtonVisible_(sites, renderedItemCount)]]"> + on-tap="onRemoveShowingSitesTap_" hidden$="[[!sites.length]]"> [[computeRemoveLabel_(filter)]] </paper-button> </div> - <template is="dom-repeat" id="list" items="[[sites]]" filter="showItem_" - rendered-item-count="{{renderedItemCount::dom-change}}" - notify-dom-change> - <div class="settings-box two-line" first$="[[!index]]" id="siteItem" - on-tap="onSiteTap_" actionable> - <div class="favicon-image" style$="[[computeSiteIcon(item.site)]]"> + <iron-list id="list" items="[[sites]]" + scroll-target="[[subpageScrollTarget]]"> + <template> + <div class="settings-box two-line site-item" first$="[[!index]]" + on-tap="onSiteTap_" actionable> + <div class="favicon-image" + style$="background-image: [[favicon_(item.site)]]"> + </div> + <div class="middle"> + [[item.site]] + <div class="secondary">[[item.localData]]</div> + </div> + <button class="subpage-arrow" is="paper-icon-button-light" + aria-label$="[[item.site]]"></button> + <div class="separator"></div> + <button is="paper-icon-button-light" class="icon-delete-gray" + title$="[[i18n('siteSettingsCookieRemoveSite', item.site)]]" + on-tap="onRemoveSiteTap_"> + </button> </div> - <div class="middle"> - [[item.site]] - <div class="secondary" id="siteSecondary">[[item.localData]]</div> - </div> - <button class="subpage-arrow" is="paper-icon-button-light" - aria-label$="[[item.site]]" - aria-describedby="siteSecondary"></button> - <div class="separator" id="removeSecondary"></div> - <button is="paper-icon-button-light" class="icon-delete-gray" - title$="[[i18n('siteSettingsCookieRemoveSite', item.site)]]" - on-tap="onRemoveSiteTap_"> - </button> - </div> - </template> + </template> + </iron-list> <!-- Confirm Delete dialog --> <dialog is="cr-dialog" id="confirmDeleteDialog" close-text="$i18n{close}" @@ -62,7 +65,7 @@ <div slot="title"> $i18n{siteSettingsCookieRemoveDialogTitle} </div> - <div slot="body">[[confirmationDeleteMsg_]]</div> + <div slot="body">$i18n{siteSettingsCookieRemoveMultipleConfirmation}</div> <div slot="button-container"> <paper-button class="cancel-button" on-tap="onCloseDialog_"> $i18n{cancel} @@ -74,6 +77,5 @@ </dialog> </template> <script src="cookie_info.js"></script> - <script src="cookie_tree_node.js"></script> <script src="site_data.js"></script> </dom-module> 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 d199c4b05c7..097465dbc0a 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_data.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_data.js @@ -8,6 +8,24 @@ */ /** + * @typedef {{ + * site: string, + * id: string, + * localData: string, + * }} + */ +var CookieDataSummaryItem; + +/** + * @typedef {{ + * id: string, + * start: number, + * count: number, + * }} + */ +var CookieRemovePacket; + +/** * TODO(dbeam): upstream to polymer externs? * @constructor * @extends {Event} @@ -21,7 +39,9 @@ Polymer({ is: 'site-data', behaviors: [ - CookieTreeBehavior, + I18nBehavior, + settings.GlobalScrollTargetBehavior, + WebUIListenerBehavior, ], properties: { @@ -29,25 +49,68 @@ Polymer({ * The current filter applied to the cookie data list. */ filter: { - observer: 'onSearchChanged_', + observer: 'updateSiteList_', notify: true, type: String, - value: '', }, - /** @private */ - confirmationDeleteMsg_: String, - /** @type {!Map<string, string>} */ focusConfig: { type: Object, observer: 'focusConfigChanged_', }, + + /** @type {!Array<!LocalDataItem>} */ + sites: { + type: Array, + value: function() { + return []; + }, + }, + + /** + * settings.GlobalScrollTargetBehavior + * @override + */ + subpageRoute: { + type: Object, + value: settings.routes.SITE_SETTINGS_SITE_DATA, + }, }, + /** @private {settings.LocalDataBrowserProxy} */ + browserProxy_: null, + /** @override */ ready: function() { - this.loadCookies(); + this.browserProxy_ = settings.LocalDataBrowserProxyImpl.getInstance(); + this.addWebUIListener( + 'on-tree-item-removed', this.updateSiteList_.bind(this)); + }, + + /** + * Reload cookies when the site data page is visited. + * + * settings.RouteObserverBehavior + * @param {!settings.Route} currentRoute + * @protected + */ + currentRouteChanged: function(currentRoute) { + settings.GlobalScrollTargetBehaviorImpl.currentRouteChanged.call( + this, currentRoute); + if (currentRoute == settings.routes.SITE_SETTINGS_SITE_DATA) { + this.browserProxy_.reloadCookies().then(this.updateSiteList_.bind(this)); + } + }, + + /** + * Returns the icon to use for a given site. + * @param {string} url The url of the site to fetch the icon for. + * @return {string} Value for background-image style. + * @private + */ + favicon_: function(url) { + return cr.icon.getFavicon(url); }, /** @@ -71,33 +134,22 @@ Polymer({ }, /** - * A filter function for the list. - * @param {!CookieDataSummaryItem} item The item to possibly filter out. - * @return {boolean} Whether to show the item. + * Gather all the site data. * @private */ - showItem_: function(item) { - if (this.filter.length == 0) - return true; - return item.site.indexOf(this.filter) > -1; - }, - - /** @private */ - onSearchChanged_: function() { - this.$.list.render(); - }, - - /** - * @return {boolean} Whether to show the multiple site remove button. - * @private - */ - isRemoveButtonVisible_: function(sites, renderedItemCount) { - return renderedItemCount != 0; + updateSiteList_: function() { + this.browserProxy_ + .getDisplayList(this.filter, 0 /* start */, -1 /* count */) + .then((listInfo) => { + this.sites = listInfo.items; + this.fire('site-data-list-complete'); + }); }, /** * Returns the string to use for the Remove label. - * @return {string} filter The current filter string. + * @param {string} filter The current filter string. + * @return {string} * @private */ computeRemoveLabel_: function(filter) { @@ -123,8 +175,6 @@ Polymer({ */ onRemoveShowingSitesTap_: function(e) { e.preventDefault(); - this.confirmationDeleteMsg_ = - loadTimeData.getString('siteSettingsCookieRemoveMultipleConfirmation'); this.$.confirmDeleteDialog.showModal(); }, @@ -134,17 +184,14 @@ Polymer({ */ onConfirmDelete_: function() { this.$.confirmDeleteDialog.close(); - if (this.filter.length == 0) { - this.removeAllCookies(); + this.browserProxy_.removeAll().then(() => { + this.sites = []; + }); } else { - var items = this.$.list.items; - for (var i = 0; i < items.length; ++i) { - if (this.showItem_(items[i])) - this.browserProxy.removeCookie(items[i].id); - } + this.browserProxy_.removeShownItems(); // We just deleted all items found by the filter, let's reset the filter. - /** @type {SettingsSubpageSearchElement} */ (this.$.filter).setValue(''); + this.fire('clear-subpage-search'); } }, @@ -155,7 +202,7 @@ Polymer({ */ onRemoveSiteTap_: function(e) { e.stopPropagation(); - this.browserProxy.removeCookie(e.model.item.id); + this.browserProxy_.removeItem(e.model.item.site); }, /** diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html b/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html index 7ab3d55d292..5544d8ffdbc 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html @@ -6,7 +6,7 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../route.html"> <link rel="import" href="../settings_shared_css.html"> -<link rel="import" href="site_settings_prefs_browser_proxy.html"> +<link rel="import" href="local_data_browser_proxy.html"> <dom-module id="site-data-details-subpage"> <template> @@ -45,6 +45,5 @@ </template> </template> <script src="cookie_info.js"></script> - <script src="cookie_tree_node.js"></script> <script src="site_data_details_subpage.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.js b/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.js index 0e913982cc0..8e8b802b64a 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.js @@ -5,6 +5,19 @@ (function() { 'use strict'; +var categoryLabels = { + app_cache: loadTimeData.getString('cookieAppCache'), + cache_storage: loadTimeData.getString('cookieCacheStorage'), + channel_id: loadTimeData.getString('cookieChannelId'), + database: loadTimeData.getString('cookieDatabaseStorage'), + file_system: loadTimeData.getString('cookieFileSystem'), + flash_lso: loadTimeData.getString('cookieFlashLso'), + indexed_db: loadTimeData.getString('cookieDatabaseStorage'), + local_storage: loadTimeData.getString('cookieLocalStorage'), + service_worker: loadTimeData.getString('cookieServiceWorker'), + media_license: loadTimeData.getString('cookieMediaLicense'), +}; + /** * 'site-data-details-subpage' Display cookie contents. */ @@ -36,17 +49,16 @@ Polymer({ /** * The browser proxy used to retrieve and change cookies. - * @private {?settings.SiteSettingsPrefsBrowserProxy} + * @private {?settings.LocalDataBrowserProxy} */ browserProxy_: null, /** @override */ ready: function() { - this.browserProxy_ = - settings.SiteSettingsPrefsBrowserProxyImpl.getInstance(); + this.browserProxy_ = settings.LocalDataBrowserProxyImpl.getInstance(); this.addWebUIListener( - 'onTreeItemRemoved', this.getCookieDetails_.bind(this)); + 'on-tree-item-removed', this.getCookieDetails_.bind(this)); }, /** @@ -59,7 +71,7 @@ Polymer({ settings.routes.SITE_SETTINGS_DATA_DETAILS) return; var site = settings.getQueryParameters().get('site'); - if (!site || site == this.site_) + if (!site) return; this.site_ = site; this.pageTitle = loadTimeData.getStringF('siteSettingsCookieSubpage', site); @@ -119,7 +131,9 @@ Polymer({ // cookie to differentiate them. if (item.type == 'cookie') return item.title; - return getCookieDataCategoryText(item.type, item.totalUsage); + if (item.type == 'quota') + return item.totalUsage; + return categoryLabels[item.type]; }, /** 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 204d19eae53..4f903171bb8 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_details.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_details.html @@ -111,12 +111,11 @@ icon="settings:sync" id="backgroundSync" label="$i18n{siteSettingsBackgroundSync}"> </site-details-permission> - <template is="dom-if" if="[[enableSoundContentSetting_]]" no-search> - <site-details-permission category="{{ContentSettingsTypes.SOUND}}" - icon="settings:volume-up" id="sound" - label="$i18n{siteSettingsSound}"> - </site-details-permission> - </template> + <site-details-permission category="{{ContentSettingsTypes.SOUND}}" + icon="settings:volume-up" id="sound" + label="$i18n{siteSettingsSound}" + hidden$="[[!enableSoundContentSetting_]]"> + </site-details-permission> <site-details-permission category="{{ContentSettingsTypes.AUTOMATIC_DOWNLOADS}}" icon="cr:file-download" id="automaticDownloads" @@ -131,6 +130,13 @@ icon="cr:extension" id="unsandboxedPlugins" label="$i18n{siteSettingsUnsandboxedPlugins}"> </site-details-permission> +<if expr="chromeos"> + <site-details-permission + category="{{ContentSettingsTypes.PROTECTED_CONTENT}}" + icon="settings:security" id="protectedContent" + label="$i18n{siteSettingsProtectedContentIdentifiers}"> + </site-details-permission> +</if> </div> <div id="clearAndReset" class="settings-box" 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 b45b86d5e2f..cc2e9aec5c9 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_details.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_details.js @@ -74,6 +74,11 @@ Polymer({ this.addWebUIListener( 'contentSettingSitePermissionChanged', this.onPermissionChanged_.bind(this)); + + // <if expr="chromeos"> + this.addWebUIListener( + 'prefEnableDrmChanged', this.prefEnableDrmChanged_.bind(this)); + // </if> }, /** @override */ @@ -108,7 +113,6 @@ Polymer({ this.updatePermissions_(this.getCategoryList_()); } }); - }, /** @@ -132,6 +136,12 @@ Polymer({ this.updatePermissions_([category]); }, + // <if expr="chromeos"> + prefEnableDrmChanged_: function() { + this.updatePermissions_([settings.ContentSettingsTypes.PROTECTED_CONTENT]); + }, + // </if> + /** * Retrieves the permissions listed in |categoryList| from the backend for * |this.origin|. @@ -222,10 +232,12 @@ Polymer({ * @private */ getCategoryList_: function() { - return Array.prototype.map.call( - this.root.querySelectorAll('site-details-permission'), (element) => { - return element.category; - }); + var categoryList = []; + this.root.querySelectorAll('site-details-permission').forEach((element) => { + if (!element.hidden) + categoryList.push(element.category); + }); + return categoryList; }, /** 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 b356f183552..c55f5716acf 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 @@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/md_select_css.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/html/web_ui_listener_behavior.html"> <link rel="import" href="../settings_shared_css.html"> @@ -13,7 +14,8 @@ <template> <style include="settings-shared md-select"></style> <div id="details"> - <div id="permissionItem" class="list-item"> + <div id="permissionItem" + class$="list-item [[permissionSourceStringClass_(site.source)]]"> <div> <iron-icon icon="[[icon]]"> </iron-icon> @@ -21,8 +23,8 @@ <div class="middle" id="permissionHeader"> [[label]] <div class="secondary" - hidden$="[[!hasPermissionSourceString_(site.source)]]"> - [[permissionSourceString_( + hidden$="[[!hasPermissionSourceString_(site.source)]]" + inner-h-t-m-l="[[permissionSourceString_( site.source, '$i18nPolymer{siteSettingsSourceEmbargo}', '$i18nPolymer{siteSettingsSourceInsecureOrigin}', @@ -32,13 +34,14 @@ '$i18nPolymer{siteSettingsSourceExtensionAsk}', '$i18nPolymer{siteSettingsSourcePolicyAllow}', '$i18nPolymer{siteSettingsSourcePolicyBlock}', - '$i18nPolymer{siteSettingsSourcePolicyAsk}')]] + '$i18nPolymer{siteSettingsSourcePolicyAsk}')]]"> </div> </div> <div class="md-select-wrapper"> <select id="permission" class="md-select" aria-labelledby="permissionHeader" - on-change="onPermissionSelectionChange_"> + on-change="onPermissionSelectionChange_" + disabled$="[[!isPermissionUserControlled_(site.source)]]"> <option id="default" value$="[[ContentSetting.DEFAULT]]"> [[defaultSettingString_( defaultSetting_, 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 26d8fd7237c..27c34c2dce2 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,7 +10,7 @@ Polymer({ is: 'site-details-permission', - behaviors: [SiteSettingsBehavior, WebUIListenerBehavior], + behaviors: [SiteSettingsBehavior, WebUIListenerBehavior, I18nBehavior], properties: { /** @@ -51,18 +51,6 @@ Polymer({ this.$.permission.value = site.setting; } - // Handle non-default sources. - if (site.source == settings.SiteSettingSource.DEFAULT || - site.source == settings.SiteSettingSource.PREFERENCE) { - this.$.permissionItem.classList.remove('two-line'); - this.$.permission.disabled = false; - } else { - this.$.permissionItem.classList.add('two-line'); - // Users are able to override embargo, so leave enabled in that case. - this.$.permission.disabled = - site.source != settings.SiteSettingSource.EMBARGO; - } - if (this.isNonDefaultAsk_(site.setting, site.source)) { assert( this.$.permission.disabled, @@ -143,6 +131,28 @@ Polymer({ }, /** + * Checks if there's a permission source string to display, and returns the + * class name to apply to permissions if so. + * @return {string} CSS class applied when there is an additional description + * string. + * @private + */ + permissionSourceStringClass_: function(source) { + return this.hasPermissionSourceString_(source) ? 'two-line' : ''; + }, + + /** + * Returns true if this permission's source is controlled by the user. + * @return {boolean} + * @private + */ + isPermissionUserControlled_: function(source) { + // Users are able override embargo. + return !this.hasPermissionSourceString_(source) || + source == settings.SiteSettingSource.EMBARGO; + }, + + /** * Returns true if the permission is set to a non-default 'ask'. Currently, * this only gets called when |this.site| is updated. * @param {!settings.ContentSetting} setting The setting of the permission. @@ -194,7 +204,18 @@ Polymer({ policyStrings[settings.ContentSetting.BLOCK] = policyBlockString; policyStrings[settings.ContentSetting.ASK] = policyAskString; - if (source == settings.SiteSettingSource.EMBARGO) { + if (source == settings.SiteSettingSource.DRM_DISABLED) { + assert( + settings.ContentSetting.BLOCK == this.site.setting, + 'If DRM is disabled, Protected Content must be blocked.'); + assert( + settings.ContentSettingsTypes.PROTECTED_CONTENT == this.category, + 'The DRM disabled source only applies to Protected Content.'); + return this.i18nAdvanced('siteSettingsSourceDrmDisabled', { + substitutions: + [settings.routes.SITE_SETTINGS_PROTECTED_CONTENT.getAbsolutePath()] + }); + } else if (source == settings.SiteSettingSource.EMBARGO) { assert( settings.ContentSetting.BLOCK == this.site.setting, 'Embargo is only used to block permissions.'); 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 899936a22cb..4f0141e4d32 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_list.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_list.html @@ -20,7 +20,11 @@ <dom-module id="site-list"> <template> - <style include="settings-shared iron-flex"></style> + <style include="settings-shared"> + .settings-row { + flex: 1 + } + </style> <div id="category"> <div class="settings-box first"> <h2 class="start">[[categoryHeader]]</h2> @@ -59,7 +63,7 @@ <div class="list-frame menu-content vertical-list" id="listContainer"> <template is="dom-repeat" items="[[sites]]"> <div class="list-item"> - <div class="settings-row wide" + <div class="settings-row" actionable$="[[enableSiteSettings_]]" on-tap="onOriginTap_"> <div class="favicon-image" style$="[[computeSiteIcon(item.origin)]]"> 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 0fe74d5f9da..6ab9c0a38a5 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 @@ -99,13 +99,6 @@ cr.define('settings', function() { setDefaultValueForContentType(contentType, defaultValue) {} /** - * Gets the cookie details for a particular site. - * @param {string} site The name of the site. - * @return {!Promise<!CookieList>} - */ - getCookieDetails(site) {} - - /** * Gets the default value for a site settings category. * @param {string} contentType The name of the category to query. * @return {!Promise<!DefaultContentSetting>} @@ -201,34 +194,6 @@ cr.define('settings', function() { setDefaultCaptureDevice(type, defaultValue) {} /** - * Reloads all cookies. - * @return {!Promise<!CookieList>} Returns the full cookie - * list. - */ - reloadCookies() {} - - /** - * Fetches all children of a given cookie. - * @param {string} path The path to the parent cookie. - * @return {!Promise<!Array<!CookieDataSummaryItem>>} Returns a cookie list - * for the given path. - */ - loadCookieChildren(path) {} - - /** - * Removes a given cookie. - * @param {string} path The path to the parent cookie. - */ - removeCookie(path) {} - - /** - * Removes all cookies. - * @return {!Promise<!CookieList>} Returns the up to date - * cookie list once deletion is complete (empty list). - */ - removeAllCookies() {} - - /** * observes _all_ of the the protocol handler state, which includes a list * that is returned through JS calls to 'setProtocolHandlers' along with * other state sent with the messages 'setIgnoredProtocolHandler' and @@ -285,7 +250,7 @@ cr.define('settings', function() { removeUsbDevice(origin, embeddingOrigin, usbDevice) {} /** - * Fetches the incognito status of the current profile (whether an icognito + * Fetches the incognito status of the current profile (whether an incognito * profile exists). Returns the results via onIncognitoStatusChanged. */ updateIncognitoStatus() {} @@ -313,11 +278,6 @@ cr.define('settings', function() { } /** @override */ - getCookieDetails(site) { - return cr.sendWithPromise('getCookieDetails', site); - } - - /** @override */ getDefaultValueForContentType(contentType) { return cr.sendWithPromise('getDefaultValueForContentType', contentType); } @@ -378,26 +338,6 @@ cr.define('settings', function() { } /** @override */ - reloadCookies() { - return cr.sendWithPromise('reloadCookies'); - } - - /** @override */ - loadCookieChildren(path) { - return cr.sendWithPromise('loadCookie', path); - } - - /** @override */ - removeCookie(path) { - chrome.send('removeCookie', [path]); - } - - /** @override */ - removeAllCookies() { - return cr.sendWithPromise('removeAllCookies'); - } - - /** @override */ observeProtocolHandlers() { chrome.send('observeProtocolHandlers'); } 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 e41cefd7258..e3354d023cb 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 @@ -115,11 +115,16 @@ Polymer({ if (key == settings.ContentSettingsTypes.USB_DEVICES || key == settings.ContentSettingsTypes.ZOOM_LEVELS) continue; - // Some values are not available (and will DCHECK) in guest mode. + // Protocol handlers are not available (and will DCHECK) in guest mode. if (this.isGuest_ && key == settings.ContentSettingsTypes.PROTOCOL_HANDLERS) { continue; } + // Similarly, protected content is only available in CrOS. + // <if expr="not chromeos"> + if (key == settings.ContentSettingsTypes.PROTECTED_CONTENT) + continue; + // </if> this.updateDefaultValueLabel_(key); } diff --git a/chromium/chrome/browser/resources/signin/signin_dice_internals/signin_dice_internals.html b/chromium/chrome/browser/resources/signin/signin_dice_internals/signin_dice_internals.html deleted file mode 100644 index 54136bcd87c..00000000000 --- a/chromium/chrome/browser/resources/signin/signin_dice_internals/signin_dice_internals.html +++ /dev/null @@ -1,51 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <link rel="import" href="chrome://resources/html/polymer.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-styles/color.html"> - <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> - <link rel="import" href="chrome://resources/html/cr.html"> - <link rel="import" href="chrome://resources/html/util.html"> - <style is="custom-style"> - .top-title-bar { - align-items: center; - border-bottom: 1px solid var(--paper-grey-300); - display: flex; - font-size: 16px; - height: 52px; - padding: 0 24px; - } - .container { - background-color: white; - color: #333; - width: 448px; - } - .action-container { - display: flex; - justify-content: flex-start; - padding: 16px; - } - paper-button.primary-action { - --paper-button-flat-keyboard-focus: { - background: rgb(58, 117, 215); - font-weight: 500; - }; - background: var(--google-blue-500); - color: white; - } - </style> - </head> - <body> - <div class="container"> - <div class="top-title-bar">Desktop Identity Consistency Internals</div> - <div class="action-container"> - <paper-button class="primary-action" id="enableSyncButton"> - Enable Sync - </paper-button> - </div> - </div> - </body> - <script src="signin_dice_internals.js"></script> -</html> diff --git a/chromium/chrome/browser/resources/signin/signin_dice_internals/signin_dice_internals.js b/chromium/chrome/browser/resources/signin/signin_dice_internals/signin_dice_internals.js deleted file mode 100644 index 4a63983482f..00000000000 --- a/chromium/chrome/browser/resources/signin/signin_dice_internals/signin_dice_internals.js +++ /dev/null @@ -1,21 +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. */ - -cr.define('signin.dice', function() { - 'use strict'; - - function initialize() { - $('enableSyncButton').addEventListener('click', onEnableSync); - } - - function onEnableSync(e) { - chrome.send('enableSync'); - } - - return { - initialize: initialize, - }; -}); - -document.addEventListener('DOMContentLoaded', signin.dice.initialize); diff --git a/chromium/chrome/browser/resources/snippets_internals.html b/chromium/chrome/browser/resources/snippets_internals.html index 9519cf97f50..07898877d79 100644 --- a/chromium/chrome/browser/resources/snippets_internals.html +++ b/chromium/chrome/browser/resources/snippets_internals.html @@ -5,7 +5,7 @@ found in the LICENSE file. --> <!DOCTYPE html> <meta charset="utf-8"> -<if expr="is_android or is_ios"> +<if expr="is_android"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </if> <title>Snippets Internals</title> @@ -92,8 +92,13 @@ found in the LICENSE file. <div id="snippets"> <h2>NTPSnippetsService</h2> <div class="forms"> - <button id="submit-download" type="button">Add snippets</button> - <span id="remote-status" class="detail"></span> + <div> + <button id="submit-download" type="button">Add snippets</button> + <span id="remote-status" class="detail"></span> + </div> + <div> + <button id="debug-log-dump" type="button">Dump the debug log</button> + </div> </div> </div> diff --git a/chromium/chrome/browser/resources/snippets_internals.js b/chromium/chrome/browser/resources/snippets_internals.js index 69a4cb5ac14..63f19c5267c 100644 --- a/chromium/chrome/browser/resources/snippets_internals.js +++ b/chromium/chrome/browser/resources/snippets_internals.js @@ -7,6 +7,7 @@ cr.define('chrome.SnippetsInternals', function() { // Stores the list of suggestions we received in receiveContentSuggestions. var lastSuggestions = []; + var lastDebugLog = ''; function initialize() { $('submit-download').addEventListener('click', function(event) { @@ -19,6 +20,11 @@ cr.define('chrome.SnippetsInternals', function() { event.preventDefault(); }); + $('debug-log-dump').addEventListener('click', function(event) { + downloadDebugLog(lastDebugLog); + event.preventDefault(); + }); + $('last-json-button').addEventListener('click', function(event) { $('last-json-container').classList.toggle('hidden'); }); @@ -66,6 +72,7 @@ cr.define('chrome.SnippetsInternals', function() { window.addEventListener('focus', refreshContent); window.setInterval(refreshContent, 1000); + chrome.send('initializationCompleted'); refreshContent(); } @@ -136,6 +143,14 @@ cr.define('chrome.SnippetsInternals', function() { } } + function receiveDebugLog(debugLog) { + if (!debugLog) { + lastDebugLog = 'empty'; + } else { + lastDebugLog = debugLog; + } + } + function receiveClassification( userClass, timeToOpenNTP, timeToShow, timeToUse) { receiveProperty('user-class', userClass); @@ -155,6 +170,10 @@ cr.define('chrome.SnippetsInternals', function() { lastRemoteSuggestionsBackgroundFetchTime); } + function receiveWhetherSuggestionPushingPossible(possible) { + $('push-dummy-suggestion-10-seconds-button').disabled = !possible; + } + function downloadJson(json) { // Redirect the browser to download data in |json| as a file "snippets.json" // (Setting Content-Disposition: attachment via a data: URL is not possible; @@ -165,6 +184,17 @@ cr.define('chrome.SnippetsInternals', function() { link.click(); } + function downloadDebugLog(debugLog) { + // Redirect the browser to download data in |debugLog| as a file + // "debug_log.txt" (Setting Content-Disposition: attachment via a data: URL + // is not possible; create a link with download attribute and simulate a + // click, instead.) + var link = document.createElement('a'); + link.download = 'debug_log.txt'; + link.href = 'data:text/plain,' + encodeURI(debugLog); + link.click(); + } + function refreshContent() { chrome.send('refreshContent'); } @@ -205,10 +235,13 @@ cr.define('chrome.SnippetsInternals', function() { receiveProperty: receiveProperty, receiveContentSuggestions: receiveContentSuggestions, receiveJson: receiveJson, + receiveDebugLog: receiveDebugLog, receiveClassification: receiveClassification, receiveRankerDebugData: receiveRankerDebugData, receiveLastRemoteSuggestionsBackgroundFetchTime: receiveLastRemoteSuggestionsBackgroundFetchTime, + receiveWhetherSuggestionPushingPossible: + receiveWhetherSuggestionPushingPossible, receiveContextualSuggestions: receiveContextualSuggestions, }; }); diff --git a/chromium/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.asciipb b/chromium/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.asciipb index f98d4385429..4c68cda3468 100644 --- a/chromium/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.asciipb +++ b/chromium/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.asciipb @@ -6,7 +6,7 @@ # certificates. See chrome/browser/ssl/ssl_error_assistant.proto for the full # format. -version_id: 3 +version_id: 4 # https://captive-portal.badssl.com leaf. # This is a test certificate, always keep it at the top. @@ -151,7 +151,128 @@ captive_portal_cert { sha256_hash: "sha256/LKtpdq9q7F7msGK0w1+b/gKoDHaQcZKTHIf9PTz2u+U=" } +# https://mitm-software.badssl.com leaf. +# This is a test certificate, keep it at the top of the MITM software list. +mitm_software { + name: "BadSSL Antivirus", + issuer_common_name_regex: "BadSSL MITM Software Test" +} + +################################################################################ +# The rest of the MITM software certificates are sorted alphabetically by name. + +mitm_software { + name: "Avast Antivirus", + issuer_common_name_regex: "avast! Web/Mail Shield Root", + issuer_organization_regex: "avast! Web/Mail Shield" +} + +mitm_software { + name: "Bitdefender Antivirus", + issuer_common_name_regex: "Bitdefender Personal CA\.Net-Defender", + issuer_organization_regex: "Bitdefender" +} + +mitm_software { + name: "Cisco Umbrella", + issuer_common_name_regex: "Cisco Umbrella Root CA", + issuer_organization_regex: "Cisco" +} + +mitm_software { + name: "Cisco Umbrella", + issuer_common_name_regex: "Cisco Umbrella Primary SubCA", + issuer_organization_regex: "Cisco" +} + +mitm_software { + name: "ContentKeeper", + issuer_common_name_regex: "ContentKeeper Appliance CA \(\d+\)", + issuer_organization_regex: "ContentKeeper Technologies" +} + +mitm_software { + name: "Cyberoam Firewall", + issuer_organization_regex: "Cyberoam Certificate Authority" +} + +mitm_software { + name: "ForcePoint", + issuer_common_name_regex: "Forcepoint Cloud CA", + issuer_organization_regex: "Forcepoint LLC" +} + +mitm_software { + name: "Fortigate", + issuer_common_name_regex: "FortiGate CA", + issuer_organization_regex: "Fortinet" +} + +mitm_software { + name: "Fortinet", + issuer_organization_regex: "Fortinet( Ltd\.)?" +} + mitm_software { name: "Kaspersky Internet Security", issuer_common_name_regex: "Kaspersky Anti-Virus Personal Root Certificate" } + +mitm_software { + name: "McAfee Web Gateway", + issuer_common_name_regex: "McAfee Web Gateway" +} + +mitm_software { + name: "NetSpark", + issuer_common_name_regex: "www\.netspark\.com", + issuer_organization_regex: "NetSpark" +} + +mitm_software { + name: "SmoothWall Firewall", + issuer_common_name_regex: "Smoothwall-default-root-certificate-authority" +} + +mitm_software { + name: "SonicWall Firewall", + issuer_organization_regex: "HTTPS Management Certificate for SonicWALL" +} + +mitm_software { + name: "Sophos", + issuer_common_name_regex: "Sophos SSL CA_[A-Z0-9\-]+", + issuer_organization_regex: "Sophos" +} + +mitm_software { + name: "Sophos", + issuer_common_name_regex: "Sophos_CA_[A-Z0-9]+" +} + +mitm_software { + name: "Sophos UTM", + issuer_common_name_regex: "sophosutm Proxy CA", + issuer_organization_regex: "sophosutm" +} + +mitm_software { + name: "Sophos Web Appliance", + issuer_common_name_regex: "Sophos Web Appliance", + issuer_organization_regex: "Sophos Plc" +} + +mitm_software { + name: "Symantec Blue Coat", + issuer_organization_regex: "Blue Coat.*" +} + +mitm_software { + name: "Trend Micro InterScan Web Security Suite (IWSS)", + issuer_common_name_regex: "IWSS\.TREND" +} + +mitm_software { + name: "Zscaler", + issuer_organization_regex: "Zscaler Inc\." +} diff --git a/chromium/chrome/browser/resources/sync_file_system_internals/main.css b/chromium/chrome/browser/resources/sync_file_system_internals/main.css index 62858f5c664..857f05218ee 100644 --- a/chromium/chrome/browser/resources/sync_file_system_internals/main.css +++ b/chromium/chrome/browser/resources/sync_file_system_internals/main.css @@ -70,7 +70,7 @@ tbody tr:nth-child(odd) { width: 18px; } -<if expr="not is_macosx and not is_ios"> +<if expr="not is_macosx"> html[dir=rtl] .file-icon-cell .folder-image { transform: scaleX(-1); } diff --git a/chromium/chrome/browser/resources/translate_internals/prefs.html b/chromium/chrome/browser/resources/translate_internals/prefs.html index 7b8aa04031d..5b84ad8da35 100644 --- a/chromium/chrome/browser/resources/translate_internals/prefs.html +++ b/chromium/chrome/browser/resources/translate_internals/prefs.html @@ -29,12 +29,10 @@ found in the LICENSE file. <h2>UX</h2> <p id="prefs-too-often-denied">Refrain from showing the translation prompt</p> </section> -<if expr="not is_ios"> <section id="prefs-cld-version"> <h2>CLD Version</h2> <p i18n-content="cld-version"></p> </section> -</if> </div> <div> <section id="prefs-dump"> diff --git a/chromium/chrome/browser/resources/uber/OWNERS b/chromium/chrome/browser/resources/uber/OWNERS deleted file mode 100644 index adf0fa51b92..00000000000 --- a/chromium/chrome/browser/resources/uber/OWNERS +++ /dev/null @@ -1 +0,0 @@ -# COMPONENT: UI>Browser>WebUI diff --git a/chromium/chrome/browser/resources/uber/uber.css b/chromium/chrome/browser/resources/uber/uber.css deleted file mode 100644 index 9831d083ca1..00000000000 --- a/chromium/chrome/browser/resources/uber/uber.css +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -body { - /* http://crbug.com/129406 --- horizontal scrollbars flicker when changing - * sections. */ - overflow-x: hidden; -} - -#navigation { - height: 100%; - left: 0; - /* This is a hack to prevent the navigation bar from occluding pointer events - * from the bottom scroll bar (which shows when one needs to horizontally - * scroll). Corresponding padding-top to offset this is in uber_frame.css */ - margin-top: -20px; - position: absolute; - /* This value is different from the left value to compensate for the scroll - * bar (which is always on and to the right) in RTL. */ - right: 15px; - width: 155px; - z-index: 3; -} - -#navigation.background { - z-index: 1; -} - -#navigation.changing-content { - transition: transform 100ms, width 100ms; -} - -.iframe-container { - -webkit-margin-start: -20px; - bottom: 0; - left: 0; - opacity: 0; - position: absolute; - right: 0; - top: 0; - transition: margin 100ms, opacity 100ms; - z-index: 1; -} - -.iframe-container.selected { - -webkit-margin-start: 0; - opacity: 1; - transition: margin 200ms, opacity 200ms; - transition-delay: 100ms; - z-index: 2; -} - -.iframe-container.expanded { - left: 0; -} - -iframe { - border: none; - display: block; - height: 100%; - width: 100%; -} diff --git a/chromium/chrome/browser/resources/uber/uber.html b/chromium/chrome/browser/resources/uber/uber.html deleted file mode 100644 index f68a151e0d7..00000000000 --- a/chromium/chrome/browser/resources/uber/uber.html +++ /dev/null @@ -1,39 +0,0 @@ -<!doctype html> -<html id="uber" class="loading" i18n-values="dir:textdirection;lang:language"> -<head> -<meta charset="utf-8"> -<title i18n-content="pageTitle"></title> -<link id="favicon" rel="icon" type="image/png" sizes="16x16"> -<link id="favicon2x" rel="icon" type="image/png" sizes="32x32"> - -<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css"> -<link rel="stylesheet" href="uber.css"> - -<script src="chrome://resources/js/cr.js"></script> -<script src="chrome://resources/js/cr/ui/focus_manager.js"></script> -<script src="chrome://resources/js/load_time_data.js"></script> -<script src="chrome://resources/js/util.js"></script> - -<script src="chrome://chrome/uber.js"></script> -<script src="chrome://chrome/uber_utils.js"></script> -</head> - -<body> - -<div id="navigation"><iframe src="chrome://uber-frame/" name="chrome" role="presentation"></iframe></div> - -<div class="iframe-container" - i18n-values="id:extensionsHost; data-url:extensionsFrameURL;" - data-favicon="IDR_EXTENSIONS_FAVICON"></div> -<div class="iframe-container" - i18n-values="id:settingsHost; data-url:settingsFrameURL;" - data-favicon="IDR_SETTINGS_FAVICON"></div> -<div class="iframe-container" - i18n-values="id:helpHost; data-url:helpFrameURL;" - data-favicon="IDR_PRODUCT_LOGO_16"></div> - -<script src="chrome://chrome/strings.js"></script> -<script src="chrome://resources/js/i18n_template.js"></script> - -</body> -</html> diff --git a/chromium/chrome/browser/resources/uber/uber.js b/chromium/chrome/browser/resources/uber/uber.js deleted file mode 100644 index 483d475c74d..00000000000 --- a/chromium/chrome/browser/resources/uber/uber.js +++ /dev/null @@ -1,476 +0,0 @@ -// Copyright (c) 2012 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. - -cr.define('uber', function() { - /** - * Options for how web history should be handled. - */ - var HISTORY_STATE_OPTION = { - PUSH: 1, // Push a new history state. - REPLACE: 2, // Replace the current history state. - NONE: 3, // Ignore this history state change. - }; - - /** - * We cache a reference to the #navigation frame here so we don't need to grab - * it from the DOM on each scroll. - * @type {Node} - * @private - */ - var navFrame; - - /** - * A queue of method invocations on one of the iframes; if the iframe has not - * loaded by the time there is a method to invoke, delay the invocation until - * it is ready. - * @type {Object} - * @private - */ - var queuedInvokes = {}; - - /** - * Handles page initialization. - */ - function onLoad(e) { - navFrame = $('navigation'); - navFrame.dataset.width = navFrame.offsetWidth; - - // Select a page based on the page-URL. - var params = resolvePageInfo(); - showPage(params.id, HISTORY_STATE_OPTION.NONE, params.path); - - window.addEventListener('message', handleWindowMessage); - window.setTimeout(function() { - document.documentElement.classList.remove('loading'); - }, 0); - - // HACK(dbeam): This makes the assumption that any second part to a path - // will result in needing background navigation. We shortcut it to avoid - // flicker on load. - // HACK(csilv): Search URLs aren't overlays, special case them. - if (params.id == 'settings' && params.path && - !params.path.startsWith('search')) { - backgroundNavigation(); - } - - ensureNonSelectedFrameContainersAreHidden(); - } - - /** - * Find page information from window.location. If the location doesn't - * point to one of our pages, return default parameters. - * @return {Object} An object containing the following parameters: - * id - The 'id' of the page. - * path - A path into the page, including search and hash. Optional. - */ - function resolvePageInfo() { - var params = {}; - var path = window.location.pathname; - if (path.length > 1) { - // Split the path into id and the remaining path. - path = path.slice(1); - var index = path.indexOf('/'); - if (index != -1) { - params.id = path.slice(0, index); - params.path = path.slice(index + 1); - } else { - params.id = path; - } - - var container = $(params.id); - if (container) { - // The id is valid. Add the hash and search parts of the URL to path. - params.path = (params.path || '') + window.location.search + - window.location.hash; - } else { - // The target sub-page does not exist, discard the params we generated. - params.id = undefined; - params.path = undefined; - } - } - // If we don't have a valid page, get a default. - if (!params.id) - params.id = getDefaultIframe().id; - - return params; - } - - /** - * Handler for window.onpopstate. - * @param {Event} e The history event. - */ - function onPopHistoryState(e) { - // Use the URL to determine which page to route to. - var params = resolvePageInfo(); - - // If the page isn't the current page, load it fresh. Even if the page is - // already loaded, it may have state not reflected in the URL, such as the - // history page's "Remove selected items" overlay. http://crbug.com/377386 - if (getRequiredElement(params.id) !== getSelectedIframeContainer()) - showPage(params.id, HISTORY_STATE_OPTION.NONE, params.path); - - // Either way, send the state down to it. - // - // Note: This assumes that the state and path parameters for every page - // under this origin are compatible. All of the downstream pages which - // navigate use pushState and replaceState. - invokeMethodOnPage(params.id, 'popState', - {state: e.state, path: '/' + params.path}); - } - - /** - * @return {Object} The default iframe container. - */ - function getDefaultIframe() { - return $(loadTimeData.getString('helpHost')); - } - - /** - * @return {Object} The currently selected iframe container. - */ - function getSelectedIframeContainer() { - return document.querySelector('.iframe-container.selected'); - } - - /** - * @return {Object} The currently selected iframe's contentWindow. - */ - function getSelectedIframeWindow() { - return getSelectedIframeContainer().querySelector('iframe').contentWindow; - } - - /** - * Handles postMessage calls from the iframes of the contained pages. - * - * The pages request functionality from this object by passing an object of - * the following form: - * - * { method : "methodToInvoke", - * params : {...} - * } - * - * |method| is required, while |params| is optional. Extra parameters required - * by a method must be specified by that method's documentation. - * - * @param {Event} e The posted object. - */ - function handleWindowMessage(e) { - e = /** @type {!MessageEvent<!{method: string, params: *}>} */(e); - if (e.data.method === 'beginInterceptingEvents') { - backgroundNavigation(); - } else if (e.data.method === 'stopInterceptingEvents') { - foregroundNavigation(); - } else if (e.data.method === 'ready') { - pageReady(e.origin); - } else if (e.data.method === 'updateHistory') { - updateHistory(e.origin, e.data.params.state, e.data.params.path, - e.data.params.replace); - } else if (e.data.method === 'setTitle') { - setTitle(e.origin, e.data.params.title); - } else if (e.data.method === 'showPage') { - showPage(e.data.params.pageId, - HISTORY_STATE_OPTION.PUSH, - e.data.params.path); - } else if (e.data.method === 'navigationControlsLoaded') { - onNavigationControlsLoaded(); - } else if (e.data.method === 'adjustToScroll') { - adjustToScroll(/** @type {number} */(e.data.params)); - } else if (e.data.method === 'mouseWheel') { - forwardMouseWheel(/** @type {Object} */(e.data.params)); - } else if (e.data.method === 'mouseDown') { - forwardMouseDown(); - } else { - console.error('Received unexpected message', e.data); - } - } - - /** - * Sends the navigation iframe to the background. - */ - function backgroundNavigation() { - navFrame.classList.add('background'); - navFrame.firstChild.tabIndex = -1; - navFrame.firstChild.setAttribute('aria-hidden', true); - } - - /** - * Retrieves the navigation iframe from the background. - */ - function foregroundNavigation() { - navFrame.classList.remove('background'); - navFrame.firstChild.tabIndex = 0; - navFrame.firstChild.removeAttribute('aria-hidden'); - } - - /** - * Enables or disables animated transitions when changing content while - * horizontally scrolled. - * @param {boolean} enabled True if enabled, else false to disable. - */ - function setContentChanging(enabled) { - navFrame.classList[enabled ? 'add' : 'remove']('changing-content'); - - if (isRTL()) { - uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow, - 'setContentChanging', enabled); - } - } - - /** - * Get an iframe based on the origin of a received post message. - * @param {string} origin The origin of a post message. - * @return {!Element} The frame associated to |origin| or null. - */ - function getIframeFromOrigin(origin) { - assert(origin.substr(-1) != '/', 'invalid origin given'); - var query = '.iframe-container > iframe[src^="' + origin + '/"]'; - var element = document.querySelector(query); - assert(element); - return /** @type {!Element} */(element); - } - - /** - * Changes the path past the page title (i.e. chrome://chrome/settings/(.*)). - * @param {Object} state The page's state object for the navigation. - * @param {string} path The new /path/ to be set after the page name. - * @param {number} historyOption The type of history modification to make. - */ - function changePathTo(state, path, historyOption) { - assert(!path || path.substr(-1) != '/', 'invalid path given'); - - var histFunc; - if (historyOption == HISTORY_STATE_OPTION.PUSH) - histFunc = window.history.pushState; - else if (historyOption == HISTORY_STATE_OPTION.REPLACE) - histFunc = window.history.replaceState; - - assert(histFunc, 'invalid historyOption given ' + historyOption); - - var pageId = getSelectedIframeContainer().id; - var args = [state, '', '/' + pageId + '/' + (path || '')]; - histFunc.apply(window.history, args); - } - - /** - * Adds or replaces the current history entry based on a navigation from the - * source iframe. - * @param {string} origin The origin of the source iframe. - * @param {Object} state The source iframe's state object. - * @param {string} path The new "path" (e.g. "/createProfile"). - * @param {boolean} replace Whether to replace the current history entry. - */ - function updateHistory(origin, state, path, replace) { - assert(!path || path[0] != '/', 'invalid path sent from ' + origin); - var historyOption = - replace ? HISTORY_STATE_OPTION.REPLACE : HISTORY_STATE_OPTION.PUSH; - // Only update the currently displayed path if this is the visible frame. - var container = getIframeFromOrigin(origin).parentNode; - if (container == getSelectedIframeContainer()) - changePathTo(state, path, historyOption); - } - - /** - * Sets the title of the page. - * @param {string} origin The origin of the source iframe. - * @param {string} title The title of the page. - */ - function setTitle(origin, title) { - // Cache the title for the client iframe, i.e., the iframe setting the - // title. querySelector returns the actual iframe element, so use parentNode - // to get back to the container. - var container = getIframeFromOrigin(origin).parentNode; - container.dataset.title = title; - - // Only update the currently displayed title if this is the visible frame. - if (container == getSelectedIframeContainer()) - document.title = title; - } - - /** - * Invokes a method on a subpage. If the subpage has not signaled readiness, - * queue the message for when it does. - * @param {string} pageId Should match an id of one of the iframe containers. - * @param {string} method The name of the method to invoke. - * @param {Object=} opt_params Optional property page of parameters to pass to - * the invoked method. - */ - function invokeMethodOnPage(pageId, method, opt_params) { - var frame = $(pageId).querySelector('iframe'); - if (!frame || !frame.hasAttribute('ready')) { - queuedInvokes[pageId] = (queuedInvokes[pageId] || []); - queuedInvokes[pageId].push([method, opt_params]); - } else { - uber.invokeMethodOnWindow(frame.contentWindow, method, opt_params); - } - } - - /** - * Called in response to a page declaring readiness. Calls any deferred method - * invocations from invokeMethodOnPage. - * @param {string} origin The origin of the source iframe. - */ - function pageReady(origin) { - var frame = getIframeFromOrigin(origin); - var container = frame.parentNode; - frame.setAttribute('ready', ''); - var queue = queuedInvokes[container.id] || []; - queuedInvokes[container.id] = undefined; - for (var i = 0; i < queue.length; i++) { - uber.invokeMethodOnWindow(frame.contentWindow, queue[i][0], queue[i][1]); - } - } - - /** - * Selects and navigates a subpage. This is called from uber-frame. - * @param {string} pageId Should match an id of one of the iframe containers. - * @param {number} historyOption Indicates whether we should push or replace - * browser history. - * @param {string} path A sub-page path. - */ - function showPage(pageId, historyOption, path) { - var container = $(pageId); - - // Lazy load of iframe contents. - var sourceUrl = container.dataset.url + (path || ''); - var frame = container.querySelector('iframe'); - if (!frame) { - frame = container.ownerDocument.createElement('iframe'); - frame.name = pageId; - frame.setAttribute('role', 'presentation'); - container.appendChild(frame); - frame.src = sourceUrl; - } else { - // There's no particularly good way to know what the current URL of the - // content frame is as we don't have access to its contentWindow's - // location, so just replace every time until necessary to do otherwise. - frame.contentWindow.location.replace(sourceUrl); - frame.removeAttribute('ready'); - } - - // If the last selected container is already showing, ignore the rest. - var lastSelected = document.querySelector('.iframe-container.selected'); - if (lastSelected === container) - return; - - if (lastSelected) { - lastSelected.classList.remove('selected'); - // Setting aria-hidden hides the container from assistive technology - // immediately. The 'hidden' attribute is set after the transition - // finishes - that ensures it's not possible to accidentally focus - // an element in an unselected container. - lastSelected.setAttribute('aria-hidden', 'true'); - } - - // Containers that aren't selected have to be hidden so that their - // content isn't focusable. - container.hidden = false; - container.setAttribute('aria-hidden', 'false'); - - // Trigger a layout after making it visible and before setting - // the class to 'selected', so that it animates in. - /** @suppress {uselessCode} */ - container.offsetTop; - container.classList.add('selected'); - - setContentChanging(true); - adjustToScroll(0); - - var selectedWindow = getSelectedIframeWindow(); - uber.invokeMethodOnWindow(selectedWindow, 'frameSelected'); - selectedWindow.focus(); - - if (historyOption != HISTORY_STATE_OPTION.NONE) - changePathTo({}, path, historyOption); - - if (container.dataset.title) - document.title = container.dataset.title; - assert('favicon' in container.dataset); - - var dataset = /** @type {{favicon: string}} */(container.dataset); - $('favicon').href = 'chrome://theme/' + dataset.favicon; - $('favicon2x').href = 'chrome://theme/' + dataset.favicon + '@2x'; - - updateNavigationControls(); - } - - function onNavigationControlsLoaded() { - updateNavigationControls(); - } - - /** - * Sends a message to uber-frame to update the appearance of the nav controls. - * It should be called whenever the selected iframe changes. - */ - function updateNavigationControls() { - var container = getSelectedIframeContainer(); - uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow, - 'changeSelection', {pageId: container.id}); - } - - /** - * Forwarded scroll offset from a content frame's scroll handler. - * @param {number} scrollOffset The scroll offset from the content frame. - */ - function adjustToScroll(scrollOffset) { - // NOTE: The scroll is reset to 0 and easing turned on every time a user - // switches frames. If we receive a non-zero value it has to have come from - // a real user scroll, so we disable easing when this happens. - if (scrollOffset != 0) - setContentChanging(false); - - if (isRTL()) { - uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow, - 'adjustToScroll', - scrollOffset); - var navWidth = Math.max(0, +navFrame.dataset.width + scrollOffset); - navFrame.style.width = navWidth + 'px'; - } else { - navFrame.style.transform = 'translateX(' + -scrollOffset + 'px)'; - } - } - - /** - * Forward scroll wheel events to subpages. - * @param {Object} params Relevant parameters of wheel event. - */ - function forwardMouseWheel(params) { - uber.invokeMethodOnWindow(getSelectedIframeWindow(), 'mouseWheel', params); - } - - /** Forward mouse down events to subpages. */ - function forwardMouseDown() { - uber.invokeMethodOnWindow(getSelectedIframeWindow(), 'mouseDown'); - } - - /** - * Make sure that iframe containers that are not selected are - * hidden, so that elements in those frames aren't part of the - * focus order. Containers that are unselected later get hidden - * when the transition ends. We also set the aria-hidden attribute - * because that hides the container from assistive technology - * immediately, rather than only after the transition ends. - */ - function ensureNonSelectedFrameContainersAreHidden() { - var containers = document.querySelectorAll('.iframe-container'); - for (var i = 0; i < containers.length; i++) { - var container = containers[i]; - if (!container.classList.contains('selected')) { - container.hidden = true; - container.setAttribute('aria-hidden', 'true'); - } - container.addEventListener('transitionend', function(event) { - if (!event.target.classList.contains('selected')) - event.target.hidden = true; - }); - } - } - - return { - onLoad: onLoad, - onPopHistoryState: onPopHistoryState - }; -}); - -window.addEventListener('popstate', uber.onPopHistoryState); -document.addEventListener('DOMContentLoaded', uber.onLoad); diff --git a/chromium/chrome/browser/resources/uber/uber_frame.css b/chromium/chrome/browser/resources/uber/uber_frame.css deleted file mode 100644 index 8445c554c7d..00000000000 --- a/chromium/chrome/browser/resources/uber/uber_frame.css +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright (c) 2012 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. */ - -html:not(.focus-outline-visible) :focus { - outline: none; -} - -body { - overflow: hidden; - padding-top: 20px; -} - -html.changing-content body { - transition: transform 100ms; -} - -h1 { - -webkit-margin-start: 23px; - color: rgb(92, 97, 102); - margin-bottom: 1em; - /* This value must match the top padding of the uber page header. */ - margin-top: 21px; -} - -ul { - list-style-type: none; - padding: 0; -} - -li { - -webkit-border-start: 6px solid transparent; - -webkit-padding-start: 18px; - cursor: pointer; - user-select: none; -} - -li:hover { - color: #777; -} - -li.selected { - -webkit-border-start-color: rgb(78, 87, 100); - cursor: default; - pointer-events: none; -} - -/* Separates the Help nav item if there are at least 3 items. */ -li:not([hidden]) ~ li:not([hidden]) ~ #help { - margin-top: 27px; -} - -button { - background-color: white; - border: 0; - color: #999; - cursor: pointer; - font: inherit; - line-height: 1.417em; /* 17px based on default 12px font size. */ - margin: 6px 0; - padding: 0; -} - -.selected > button { - color: rgb(70, 78, 90); -} - -html[guestMode='true'] .hide-in-guest { - display: none; -} diff --git a/chromium/chrome/browser/resources/uber/uber_frame.html b/chromium/chrome/browser/resources/uber/uber_frame.html deleted file mode 100644 index 05c89a7749b..00000000000 --- a/chromium/chrome/browser/resources/uber/uber_frame.html +++ /dev/null @@ -1,39 +0,0 @@ -<!doctype html> -<html i18n-values="dir:textdirection;lang:language;guestMode:profileIsGuest" - id="uber"> -<head> -<meta charset="utf-8"> - -<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css"> -<link rel="stylesheet" href="uber_frame.css"> - -<script src="chrome://resources/js/cr.js"></script> -<script src="chrome://resources/js/cr/ui/focus_manager.js"></script> -<script src="chrome://resources/js/cr/ui/focus_outline_manager.js"></script> -<script src="chrome://resources/js/load_time_data.js"></script> -<script src="chrome://uber-frame/uber_frame.js"></script> -</head> - -<body> - -<h1 i18n-content="shortProductName"></h1> -<ul role="tablist"> - <li i18n-values="controls:extensionsHost" role="tab" id="extensions" - class="hide-in-guest"> - <button class="custom-appearance" - i18n-content="extensionsDisplayName"></button> - </li> - <li i18n-values="controls:settingsHost" role="tab" id="settings"> - <button class="custom-appearance" - i18n-content="settingsDisplayName"></button> - </li> - <li i18n-values="controls:helpHost" role="tab" id="help"> - <button class="custom-appearance" i18n-content="helpDisplayName"></button> - </li> -</ul> - -<script src="chrome://uber-frame/strings.js"></script> -<script src="chrome://resources/js/i18n_template.js"></script> - -</body> -</html> diff --git a/chromium/chrome/browser/resources/uber/uber_frame.js b/chromium/chrome/browser/resources/uber/uber_frame.js deleted file mode 100644 index b60feaed92b..00000000000 --- a/chromium/chrome/browser/resources/uber/uber_frame.js +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) 2012 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. - -// This file contains the navigation controls that are visible on the left side -// of the uber page. It exists separately from uber.js so that it may be loaded -// in an iframe. Iframes can be layered on top of each other, but not mixed in -// with page content, so all overlapping content on uber must be framed. - -// <include src="../../../../ui/webui/resources/js/util.js"> -// <include src="uber_utils.js"> - -cr.define('uber_frame', function() { - - /** - * Handles page initialization. - */ - function onLoad() { - var navigationItems = document.querySelectorAll('li'); - - for (var i = 0; i < navigationItems.length; ++i) { - navigationItems[i].addEventListener('click', onNavItemClicked); - } - - cr.ui.FocusOutlineManager.forDocument(this); - - window.addEventListener('message', handleWindowMessage); - uber.invokeMethodOnParent('navigationControlsLoaded'); - - document.documentElement.addEventListener('mousewheel', onMouseWheel); - document.documentElement.addEventListener('mousedown', onMouseDown); - } - - /** - * Handles clicks on the navigation controls (switches the page and updates - * the URL). - * @param {Event} e The click event. - */ - function onNavItemClicked(e) { - // Though pointer-event: none; is applied to the .selected nav item, users - // can still tab to them and press enter/space which simulates a click. - if (e.target.classList.contains('selected')) - return; - - uber.invokeMethodOnParent('showPage', - {pageId: e.currentTarget.getAttribute('controls')}); - - setSelection(/** @type {Element} */(e.currentTarget)); - } - - /** - * Handles postMessage from chrome://chrome. - * @param {Event} e The post data. - */ - function handleWindowMessage(e) { - if (e.data.method === 'changeSelection') - changeSelection(e.data.params); - else if (e.data.method === 'adjustToScroll') - adjustToScroll(e.data.params); - else if (e.data.method === 'setContentChanging') - setContentChanging(e.data.params); - else - console.error('Received unexpected message', e.data); - } - - /** - * Changes the selected nav control. - * @param {Object} params Must contain pageId. - */ - function changeSelection(params) { - var navItem = - document.querySelector('li[controls="' + params.pageId + '"]'); - setSelection(navItem); - showNavItems(); - } - - /** - * @return {Element} The currently selected nav item, if any. - */ - function getSelectedNavItem() { - return document.querySelector('li.selected'); - } - - /** - * Sets selection on the given nav item. - * @param {Element} newSelection The item to be selected. - */ - function setSelection(newSelection) { - var items = document.querySelectorAll('li'); - for (var i = 0; i < items.length; ++i) { - items[i].classList.toggle('selected', items[i] == newSelection); - items[i].setAttribute('aria-selected', items[i] == newSelection); - } - } - - /** - * Shows nav items belonging to the same group as the selected item. - */ - function showNavItems() { - var hideSettingsAndHelp = loadTimeData.getBoolean('hideSettingsAndHelp'); - $('settings').hidden = hideSettingsAndHelp; - $('help').hidden = hideSettingsAndHelp; - $('extensions').hidden = loadTimeData.getBoolean('hideExtensions'); - } - - /** - * Adjusts this frame's content to scrolls from the outer frame. This is done - * to obscure text in RTL as a user scrolls over the content of this frame (as - * currently RTL scrollbars still draw on the right). - * @param {number} scrollLeft document.body.scrollLeft of the content frame. - */ - function adjustToScroll(scrollLeft) { - assert(isRTL()); - document.body.style.transform = 'translateX(' + -scrollLeft + 'px)'; - } - - /** - * Enable/disable an animation to ease the nav bar back into view when - * changing content while horizontally scrolled. - * @param {boolean} enabled Whether easing should be enabled. - */ - function setContentChanging(enabled) { - assert(isRTL()); - document.documentElement.classList.toggle('changing-content', enabled); - } - - /** - * Handles mouse wheels on the top level element. Forwards them to uber.js. - * @param {Event} e The mouse wheel event. - */ - function onMouseWheel(e) { - uber.invokeMethodOnParent('mouseWheel', - {deltaX: e.wheelDeltaX, deltaY: e.wheelDeltaY}); - } - - /** - * Handles mouse presses on the top level element. Forwards them to uber.js. - * @param {Event} e The mouse down event. - */ - function onMouseDown(e) { - uber.invokeMethodOnParent('mouseDown'); - } - - /** - * @return {Element} The currently selected iframe container. - * @private - */ - function getSelectedIframe() { - return document.querySelector('.iframe-container.selected'); - } - - return {onLoad: onLoad}; -}); - -document.addEventListener('DOMContentLoaded', uber_frame.onLoad); diff --git a/chromium/chrome/browser/resources/uber/uber_page_manager_observer.js b/chromium/chrome/browser/resources/uber/uber_page_manager_observer.js deleted file mode 100644 index effea4e48b2..00000000000 --- a/chromium/chrome/browser/resources/uber/uber_page_manager_observer.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2014 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. - -cr.define('uber', function() { - var PageManager = cr.ui.pageManager.PageManager; - - /** - * A PageManager observer that updates the uber page. - * @constructor - * @implements {cr.ui.pageManager.PageManager.Observer} - */ - function PageManagerObserver() {} - - PageManagerObserver.prototype = { - __proto__: PageManager.Observer.prototype, - - /** - * Informs the uber page when a top-level overlay is opened or closed. - * @param {cr.ui.pageManager.Page} page The page that is being shown or was - * hidden. - * @override - */ - onPageVisibilityChanged: function(page) { - if (PageManager.isTopLevelOverlay(page)) { - if (page.visible) - uber.invokeMethodOnParent('beginInterceptingEvents'); - else - uber.invokeMethodOnParent('stopInterceptingEvents'); - } - }, - - /** - * Uses uber to set the title. - * @param {string} title The title to set. - * @override - */ - updateTitle: function(title) { - uber.setTitle(title); - }, - - /** - * Pushes the current page onto the history stack, replacing the current - * entry if appropriate. - * @param {string} path The path of the page to push onto the stack. - * @param {boolean} replace If true, allow no history events to be created. - * @override - */ - updateHistory: function(path, replace) { - var historyFunction = replace ? uber.replaceState : uber.pushState; - historyFunction({}, path); - }, - }; - - // Export - return { - PageManagerObserver: PageManagerObserver - }; -}); diff --git a/chromium/chrome/browser/resources/uber/uber_utils.js b/chromium/chrome/browser/resources/uber/uber_utils.js deleted file mode 100644 index e1db645b2f9..00000000000 --- a/chromium/chrome/browser/resources/uber/uber_utils.js +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright (c) 2012 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 collection of utility methods for UberPage and its contained - * pages. - */ - -cr.define('uber', function() { - /** - * Fixed position header elements on the page to be shifted by handleScroll. - * @type {NodeList} - */ - var headerElements; - - /** - * This should be called by uber content pages when DOM content has loaded. - */ - function onContentFrameLoaded() { - headerElements = document.getElementsByTagName('header'); - document.addEventListener('scroll', handleScroll); - document.addEventListener('mousedown', handleMouseDownInFrame, true); - - invokeMethodOnParent('ready'); - - // Prevent the navigation from being stuck in a disabled state when a - // content page is reloaded while an overlay is visible (crbug.com/246939). - invokeMethodOnParent('stopInterceptingEvents'); - - // Trigger the scroll handler to tell the navigation if our page started - // with some scroll (happens when you use tab restore). - handleScroll(); - - window.addEventListener('message', handleWindowMessage); - } - - /** - * Handles scroll events on the document. This adjusts the position of all - * headers and updates the parent frame when the page is scrolled. - */ - function handleScroll() { - var scrollLeft = scrollLeftForDocument(document); - var offset = scrollLeft * -1; - for (var i = 0; i < headerElements.length; i++) { - // As a workaround for http://crbug.com/231830, set the transform to - // 'none' rather than 0px. - headerElements[i].style.transform = offset ? - 'translateX(' + offset + 'px)' : 'none'; - } - - invokeMethodOnParent('adjustToScroll', scrollLeft); - } - - /** - * Tells the parent to focus the current frame if the mouse goes down in the - * current frame (and it doesn't already have focus). - * @param {Event} e A mousedown event. - */ - function handleMouseDownInFrame(e) { - if (!e.isSynthetic && !document.hasFocus()) - window.focus(); - } - - /** - * Handles 'message' events on window. - * @param {Event} e The message event. - */ - function handleWindowMessage(e) { - e = /** @type {!MessageEvent<!{method: string, params: *}>} */(e); - if (e.data.method === 'frameSelected') { - handleFrameSelected(); - } else if (e.data.method === 'mouseWheel') { - handleMouseWheel( - /** @type {{deltaX: number, deltaY: number}} */(e.data.params)); - } else if (e.data.method === 'mouseDown') { - handleMouseDown(); - } else if (e.data.method === 'popState') { - handlePopState(e.data.params.state, e.data.params.path); - } - } - - /** - * This is called when a user selects this frame via the navigation bar - * frame (and is triggered via postMessage() from the uber page). - */ - function handleFrameSelected() { - setScrollTopForDocument(document, 0); - } - - /** - * Called when a user mouse wheels (or trackpad scrolls) over the nav frame. - * The wheel event is forwarded here and we scroll the body. - * There's no way to figure out the actual scroll amount for a given delta. - * It differs for every platform and even initWebKitWheelEvent takes a - * pixel amount instead of a wheel delta. So we just choose something - * reasonable and hope no one notices the difference. - * @param {{deltaX: number, deltaY: number}} params A structure that holds - * wheel deltas in X and Y. - */ - function handleMouseWheel(params) { - window.scrollBy(-params.deltaX * 49 / 120, -params.deltaY * 49 / 120); - } - - /** - * Fire a synthetic mousedown on the body to dismiss transient things like - * bubbles or menus that listen for mouse presses outside of their UI. We - * dispatch a fake mousedown rather than a 'mousepressedinnavframe' so that - * settings/history/extensions don't need to know about their embedder. - */ - function handleMouseDown() { - var mouseEvent = new MouseEvent('mousedown'); - mouseEvent.isSynthetic = true; - document.dispatchEvent(mouseEvent); - } - - /** - * Called when the parent window restores some state saved by uber.pushState - * or uber.replaceState. Simulates a popstate event. - * @param {PopStateEvent} state A state object for replaceState and pushState. - * @param {string} path The path the page navigated to. - * @suppress {checkTypes} - */ - function handlePopState(state, path) { - window.history.replaceState(state, '', path); - window.dispatchEvent(new PopStateEvent('popstate', {state: state})); - } - - /** - * @return {boolean} Whether this frame has a parent. - */ - function hasParent() { - return window != window.parent; - } - - /** - * Invokes a method on the parent window (UberPage). This is a convenience - * method for API calls into the uber page. - * @param {string} method The name of the method to invoke. - * @param {?=} opt_params Optional property bag of parameters to pass to the - * invoked method. - */ - function invokeMethodOnParent(method, opt_params) { - if (!hasParent()) - return; - - invokeMethodOnWindow(window.parent, method, opt_params, 'chrome://chrome'); - } - - /** - * Invokes a method on the target window. - * @param {string} method The name of the method to invoke. - * @param {?=} opt_params Optional property bag of parameters to pass to the - * invoked method. - * @param {string=} opt_url The origin of the target window. - */ - function invokeMethodOnWindow(targetWindow, method, opt_params, opt_url) { - var data = {method: method, params: opt_params}; - targetWindow.postMessage(data, opt_url ? opt_url : '*'); - } - - /** - * Updates the page's history state. If the page is embedded in a child, - * forward the information to the parent for it to manage history for us. This - * is a replacement of history.replaceState and history.pushState. - * @param {Object} state A state object for replaceState and pushState. - * @param {string} path The path the page navigated to. - * @param {boolean} replace If true, navigate with replacement. - */ - function updateHistory(state, path, replace) { - var historyFunction = replace ? - window.history.replaceState : - window.history.pushState; - - if (hasParent()) { - // If there's a parent, always replaceState. The parent will do the actual - // pushState. - historyFunction = window.history.replaceState; - invokeMethodOnParent('updateHistory', { - state: state, path: path, replace: replace}); - } - historyFunction.call(window.history, state, '', '/' + path); - } - - /** - * Sets the current title for the page. If the page is embedded in a child, - * forward the information to the parent. This is a replacement for setting - * document.title. - * @param {string} title The new title for the page. - */ - function setTitle(title) { - document.title = title; - invokeMethodOnParent('setTitle', {title: title}); - } - - /** - * Pushes new history state for the page. If the page is embedded in a child, - * forward the information to the parent; when embedded, all history entries - * are attached to the parent. This is a replacement of history.pushState. - * @param {Object} state A state object for replaceState and pushState. - * @param {string} path The path the page navigated to. - */ - function pushState(state, path) { - updateHistory(state, path, false); - } - - /** - * Replaces the page's history state. If the page is embedded in a child, - * forward the information to the parent; when embedded, all history entries - * are attached to the parent. This is a replacement of history.replaceState. - * @param {Object} state A state object for replaceState and pushState. - * @param {string} path The path the page navigated to. - */ - function replaceState(state, path) { - updateHistory(state, path, true); - } - - return { - invokeMethodOnParent: invokeMethodOnParent, - invokeMethodOnWindow: invokeMethodOnWindow, - onContentFrameLoaded: onContentFrameLoaded, - pushState: pushState, - replaceState: replaceState, - setTitle: setTitle, - }; -}); diff --git a/chromium/chrome/browser/resources/uber/uber_shared.css b/chromium/chrome/browser/resources/uber_shared.css index a53533c2af0..1aa69c4a5f3 100644 --- a/chromium/chrome/browser/resources/uber/uber_shared.css +++ b/chromium/chrome/browser/resources/uber_shared.css @@ -173,7 +173,7 @@ body .section-header > h3 { .page-banner-text { -webkit-padding-end: 8px; -webkit-padding-start: 26px; - background-image: url(../../../../ui/webui/resources/images/business.svg); + background-image: url(../../../ui/webui/resources/images/business.svg); background-position: 0 center; background-repeat: no-repeat; background-size: 18px; diff --git a/chromium/chrome/browser/resources/unpack_pak.py b/chromium/chrome/browser/resources/unpack_pak.py index cf747600db7..8eda2e871fb 100755 --- a/chromium/chrome/browser/resources/unpack_pak.py +++ b/chromium/chrome/browser/resources/unpack_pak.py @@ -11,6 +11,10 @@ import sys _HERE_PATH = os.path.join(os.path.dirname(__file__)) +# The name of a dummy file to be updated always after all other files have been +# written. This file is declared as the "output" for GN's purposes +_TIMESTAMP_FILENAME = os.path.join('unpack.stamp') + _SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..', '..')) sys.path.insert(1, os.path.join(_SRC_PATH, 'tools', 'grit')) @@ -62,6 +66,10 @@ def main(): unpack(args.pak_file, args.out_folder) + timestamp_file_path = os.path.join(args.out_folder, _TIMESTAMP_FILENAME) + with open(timestamp_file_path, 'a'): + os.utime(timestamp_file_path, None) + if __name__ == '__main__': main() |