diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-01-29 16:35:13 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-02-01 15:33:35 +0000 |
commit | c8c2d1901aec01e934adf561a9fdf0cc776cdef8 (patch) | |
tree | 9157c3d9815e5870799e070b113813bec53e0535 /chromium/chrome/browser/resources | |
parent | abefd5095b41dac94ca451d784ab6e27372e981a (diff) |
BASELINE: Update Chromium to 64.0.3282.139
Change-Id: I1cae68fe9c94ff7608b26b8382fc19862cdb293a
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/chrome/browser/resources')
472 files changed, 12567 insertions, 10706 deletions
diff --git a/chromium/chrome/browser/resources/BUILD.gn b/chromium/chrome/browser/resources/BUILD.gn index b2d10f2a0ba..f0559f28e62 100644 --- a/chromium/chrome/browser/resources/BUILD.gn +++ b/chromium/chrome/browser/resources/BUILD.gn @@ -27,16 +27,6 @@ grit("net_internals_resources") { output_dir = "$root_gen_dir/chrome" } -grit("password_manager_internals_resources") { - source = "password_manager_internals_resources.grd" - defines = chrome_grit_defines - outputs = [ - "grit/password_manager_internals_resources.h", - "password_manager_internals_resources.pak", - ] - output_dir = "$root_gen_dir/chrome" -} - grit("quota_internals_resources") { source = "quota_internals_resources.grd" defines = chrome_grit_defines @@ -163,6 +153,20 @@ if (enable_extensions) { } } +if (enable_print_preview) { + grit("print_preview_resources") { + source = "print_preview/print_preview_resources.grd" + defines = chrome_grit_defines + outputs = [ + "grit/print_preview_resources.h", + "grit/print_preview_resources_map.cc", + "grit/print_preview_resources_map.h", + "print_preview_resources.pak", + ] + output_dir = "$root_gen_dir/chrome" + } +} + if (enable_vr) { grit("vr_shell_resources") { source = "vr_shell_resources.grd" diff --git a/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.css b/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.css index c0f225af31f..55b21cbfd64 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.css +++ b/chromium/chrome/browser/resources/bluetooth_internals/bluetooth_internals.css @@ -26,6 +26,10 @@ h1 { color: rgb(92, 97, 102); } +.info-container h4 button.show-all-properties { + margin: 10px; +} + .toggle-status { background-image: url(../../../../ui/webui/resources/images/cancel_red.svg); background-repeat: no-repeat; diff --git a/chromium/chrome/browser/resources/bluetooth_internals/characteristic_list.js b/chromium/chrome/browser/resources/bluetooth_internals/characteristic_list.js index ecee19eb0fe..c2372998a34 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/characteristic_list.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/characteristic_list.js @@ -88,6 +88,7 @@ cr.define('characteristic_list', function() { this.propertiesFieldSet_.setPropertyDisplayNames( PROPERTIES_PROPERTY_NAMES); var Property = bluetooth.mojom.Property; + this.propertiesFieldSet_.showAll = false; this.propertiesFieldSet_.setObject({ broadcast: (this.info.properties & Property.BROADCAST) > 0, read: (this.info.properties & Property.READ) > 0, @@ -148,6 +149,17 @@ cr.define('characteristic_list', function() { var propertiesHeader = document.createElement('h4'); propertiesHeader.textContent = 'Properties'; + var propertiesBtn = document.createElement('button'); + propertiesBtn.textContent = 'Show All'; + propertiesBtn.classList.add('show-all-properties'); + propertiesBtn.addEventListener('click', () => { + this.propertiesFieldSet_.showAll = !this.propertiesFieldSet_.showAll; + propertiesBtn.textContent = + this.propertiesFieldSet_.showAll ? 'Hide' : 'Show all'; + this.propertiesFieldSet_.redraw(); + }); + propertiesHeader.appendChild(propertiesBtn); + var propertiesDiv = document.createElement('div'); propertiesDiv.classList.add('flex'); propertiesDiv.appendChild(this.propertiesFieldSet_); diff --git a/chromium/chrome/browser/resources/bluetooth_internals/object_fieldset.js b/chromium/chrome/browser/resources/bluetooth_internals/object_fieldset.js index f433c6f2629..c28fb097ad1 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/object_fieldset.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/object_fieldset.js @@ -26,6 +26,14 @@ cr.define('object_fieldset', function() { ObjectFieldSet.prototype = { __proto__: HTMLFieldSetElement.prototype, + set showAll(showAll) { + this.showAll_ = showAll; + }, + + get showAll() { + return this.showAll_; + }, + /** * Decorates the element as an ObjectFieldset. */ @@ -36,6 +44,7 @@ cr.define('object_fieldset', function() { this.value = null; /** @private {?Object<string, string>} */ this.nameMap_ = null; + this.showAll_ = true; }, /** @@ -63,8 +72,11 @@ cr.define('object_fieldset', function() { this.innerHTML = ''; Object.keys(this.value).forEach(function(propName) { - var name = this.nameMap_[propName] || propName; var value = this.value[propName]; + if (value === false && !this.showAll_) + return; + + var name = this.nameMap_[propName] || propName; var newField = document.createElement('div'); newField.classList.add('status'); diff --git a/chromium/chrome/browser/resources/bluetooth_internals/value_control.js b/chromium/chrome/browser/resources/bluetooth_internals/value_control.js index 69dfc188cf8..9a8ea9133f2 100644 --- a/chromium/chrome/browser/resources/bluetooth_internals/value_control.js +++ b/chromium/chrome/browser/resources/bluetooth_internals/value_control.js @@ -295,7 +295,9 @@ cr.define('value_control', function() { this.readBtn_.hidden = (this.properties_ & bluetooth.mojom.Property.READ) === 0; this.writeBtn_.hidden = - (this.properties_ & bluetooth.mojom.Property.WRITE) === 0; + (this.properties_ & bluetooth.mojom.Property.WRITE) === 0 && + (this.properties_ & + bluetooth.mojom.Property.WRITE_WITHOUT_RESPONSE) === 0; var isAvailable = !this.readBtn_.hidden || !this.writeBtn_.hidden; this.unavailableMessage_.hidden = isAvailable; 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 dbbb3c6f00a..ed7b45e0e20 100644 --- a/chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd +++ b/chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd @@ -979,7 +979,7 @@ <message desc="Spoken in PowerKey if there are no jumps to display." name="IDS_CHROMEVOX_POWERKEY_NO_JUMPS"> No jumps. </message> - <message desc="Describes the list position of a list item." name="IDS_CHROMEVOX_LIST_POSITION"> + <message desc="Describes the list position of a list item in spoken feedback." name="IDS_CHROMEVOX_LIST_POSITION"> <ph name="index">$1</ph> of <ph name="total">$2</ph> </message> <message desc="Describes the list position of a list item in braille." name="IDS_CHROMEVOX_LIST_POSITION_BRL"> diff --git a/chromium/chrome/browser/resources/chromeos/compiled_resources2.gyp b/chromium/chrome/browser/resources/chromeos/compiled_resources2.gyp index 20fea0a35fc..2aff6fece17 100644 --- a/chromium/chrome/browser/resources/chromeos/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/chromeos/compiled_resources2.gyp @@ -4,7 +4,7 @@ { 'targets': [ { - 'target_name': 'bluetooth_dialog_host', + 'target_name': 'bluetooth_pairing_dialog', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', @@ -20,6 +20,7 @@ '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/cr_dialog/compiled_resources2.gyp:cr_dialog', '<(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', diff --git a/chromium/chrome/browser/resources/chromeos/input_method/google_xkb_manifest.json b/chromium/chrome/browser/resources/chromeos/input_method/google_xkb_manifest.json index ed509c9735b..a18e51cd59f 100644 --- a/chromium/chrome/browser/resources/chromeos/input_method/google_xkb_manifest.json +++ b/chromium/chrome/browser/resources/chromeos/input_method/google_xkb_manifest.json @@ -410,7 +410,9 @@ "description": "", "language": [ "de", - "de-DE" + "de-AT", + "de-DE", + "de-LI" ], "layouts": [ "de" @@ -426,7 +428,9 @@ "description": "", "language": [ "de", - "de-DE" + "de-AT", + "de-DE", + "de-LI" ], "layouts": [ "de(neo)" diff --git a/chromium/chrome/browser/resources/chromeos/login/BUILD.gn b/chromium/chrome/browser/resources/chromeos/login/BUILD.gn new file mode 100644 index 00000000000..8ad38cb8443 --- /dev/null +++ b/chromium/chrome/browser/resources/chromeos/login/BUILD.gn @@ -0,0 +1,30 @@ +# 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. + +import("//third_party/closure_compiler/compile_js.gni") + +group("closure_compile") { + deps = [ + ":offline_ad_login", + ":oobe_change_picture", + ] +} + +js_binary("offline_ad_login") { + deps = [ + "//ui/webui/resources/js:load_time_data", + ] +} + +js_binary("oobe_change_picture") { + deps = [ + "//ui/webui/resources/cr_elements/chromeos/cr_picture:cr_picture_list", + "//ui/webui/resources/cr_elements/chromeos/cr_picture:cr_picture_pane", + "//ui/webui/resources/cr_elements/chromeos/cr_picture:cr_picture_types", + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:i18n_behavior", + "//ui/webui/resources/js:load_time_data", + "//ui/webui/resources/js:util", + ] +} diff --git a/chromium/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn b/chromium/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn index 9be56005408..a30ce42d14c 100644 --- a/chromium/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn +++ b/chromium/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn @@ -29,6 +29,7 @@ run_jsbundler("select_to_speak_copied_files") { "checked.png", "options.css", "options.html", + "paragraph_utils.js", "select_to_speak.js", "select_to_speak_gdocs_script.js", "select_to_speak_main.js", diff --git a/chromium/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp b/chromium/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp index 7920ad89a63..a5811052311 100644 --- a/chromium/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp @@ -7,9 +7,11 @@ 'target_name': 'select_to_speak', 'dependencies': [ 'externs', + 'paragraph_utils', '<(EXTERNS_GYP):accessibility_private', '<(EXTERNS_GYP):automation', '<(EXTERNS_GYP):chrome_extensions', + '<(EXTERNS_GYP):metrics_private', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -20,6 +22,7 @@ '<(EXTERNS_GYP):accessibility_private', '<(EXTERNS_GYP):automation', '<(EXTERNS_GYP):chrome_extensions', + '<(EXTERNS_GYP):metrics_private', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -27,5 +30,15 @@ 'target_name': 'externs', 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, + { + 'target_name': 'paragraph_utils', + 'dependencies': [ + 'externs', + '<(EXTERNS_GYP):accessibility_private', + '<(EXTERNS_GYP):automation', + '<(EXTERNS_GYP):chrome_extensions', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, ], } diff --git a/chromium/chrome/browser/resources/chromeos/select_to_speak/strings/select_to_speak_strings.grd b/chromium/chrome/browser/resources/chromeos/select_to_speak/strings/select_to_speak_strings.grd index 5518b2f7ba1..c78c65c209e 100644 --- a/chromium/chrome/browser/resources/chromeos/select_to_speak/strings/select_to_speak_strings.grd +++ b/chromium/chrome/browser/resources/chromeos/select_to_speak/strings/select_to_speak_strings.grd @@ -153,6 +153,36 @@ <message desc="Label for the fastest synthesized speech rate in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_RATE_FASTEST"> Fastest </message> + <message desc="Group of options controlling highlighting." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT"> + Highlighting + </message> + <message desc="Label for option to highlight spoken words rather than spoken nodes." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_DESCRIPTION"> + Highlight each word as it is spoken + </message> + <message desc="Label for option to pick word highlight color." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_DESCRIPTION"> + Color for word highlights: + </message> + <message desc="Label for a blue highlight color in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_BLUE"> + Blue + </message> + <message desc="Label for a orange highlight color in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_ORANGE"> + Orange + </message> + <message desc="Label for a yellow highlight color in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_YELLOW"> + Yellow + </message> + <message desc="Label for a green highlight color in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_GREEN"> + Green + </message> + <message desc="Label for a pink highlight color in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_PINK"> + Pink + </message> + <message desc="Example of a word highlight on a dark background in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_DARK"> + Dark background + </message> + <message desc="Example of a word highlight on a light background in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_LIGHT"> + Light background + </message> </messages> </release> </grit> diff --git a/chromium/chrome/browser/resources/component_extension_resources.grd b/chromium/chrome/browser/resources/component_extension_resources.grd index 6ec5019a7ff..1a7f9514b0c 100644 --- a/chromium/chrome/browser/resources/component_extension_resources.grd +++ b/chromium/chrome/browser/resources/component_extension_resources.grd @@ -13,7 +13,6 @@ <release seq="1"> <structures> <structure name="IDR_BOOKMARK_MANAGER_MAIN" file="bookmark_manager/main.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" /> - <structure name="IDR_HOTWORD_AUDIO_VERIFICATION_MAIN" file="hotword_audio_verification/main.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" /> <if expr="chromeos"> <structure name="IDR_WALLPAPER_MANAGER_MAIN" file="chromeos/wallpaper_manager/main.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" /> <if expr="not _google_chrome"> @@ -48,49 +47,6 @@ <include name="IDR_HANGOUT_SERVICES_BACKGROUND_HTML" file="hangout_services/background.html" type="BINDATA" /> <include name="IDR_HANGOUT_SERVICES_THUNK_JS" file="hangout_services/thunk.js" type="BINDATA" /> </if> - <if expr="enable_extensions"> - <!-- Hotword Audio Verification app --> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_BACKGROUND_JS" file="hotword_audio_verification/event_page.js" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_MAIN_JS" file="hotword_audio_verification/main.js" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_FLOW_JS" file="hotword_audio_verification/flow.js" type="BINDATA" /> - <include name="IDR_START_STEP_HTML" file="hotword_audio_verification/steps/start_step.html" flattenhtml="true" type="BINDATA" /> - <include name="IDR_AUDIO_HISTORY_STEP_HTML" file="hotword_audio_verification/steps/audio_history_step.html" flattenhtml="true" type="BINDATA" /> - <include name="IDR_SPEECH_TRAINING_STEP_HTML" file="hotword_audio_verification/steps/speech_training_step.html" flattenhtml="true" type="BINDATA" /> - <include name="IDR_FINISHED_STEP_HTML" file="hotword_audio_verification/steps/finished_step.html" flattenhtml="true" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_STYLE_CSS" file="hotword_audio_verification/style.css" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_CLOSE_1X" file="hotword_audio_verification/images/ic-x-white-1x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_CLOSE_2X" file="hotword_audio_verification/images/ic-x-white-2x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_INTRO_1X" file="hotword_audio_verification/images/intro-1x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_INTRO_2X" file="hotword_audio_verification/images/intro-2x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_HEADER_1X" file="hotword_audio_verification/images/gradient-1x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_HEADER_2X" file="hotword_audio_verification/images/gradient-2x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_CHECK_BLUE_1X" file="hotword_audio_verification/images/ic-check-blue-1x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_CHECK_BLUE_2X" file="hotword_audio_verification/images/ic-check-blue-2x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_CHECK_GRAY_1X" file="hotword_audio_verification/images/ic-check-gray-1x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_CHECK_GRAY_2X" file="hotword_audio_verification/images/ic-check-gray-2x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_LOADER_1X" file="hotword_audio_verification/images/placeholder-loader-1x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_LOADER_2X" file="hotword_audio_verification/images/placeholder-loader-2x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_ERROR_1X" file="hotword_audio_verification/images/ic-error-1x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_ERROR_2X" file="hotword_audio_verification/images/ic-error-2x.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_ICON_16" file="hotword_audio_verification/images/icon-16.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_ICON_48" file="hotword_audio_verification/images/icon-48.png" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_VERIFICATION_IMAGE_ICON_128" file="hotword_audio_verification/images/icon-128.png" type="BINDATA" /> - - <!-- Hotword extension --> - <include name="IDR_HOTWORD_ALWAYS_ON_MANAGER_JS" file="hotword/always_on_manager.js" type="BINDATA" /> - <include name="IDR_HOTWORD_AUDIO_CLIENT_JS" file="hotword/audio_client.js" type="BINDATA" /> - <include name="IDR_HOTWORD_BASE_SESSION_MANAGER_JS" file="hotword/base_session_manager.js" type="BINDATA" /> - <include name="IDR_HOTWORD_CONSTANTS_JS" file="hotword/constants.js" type="BINDATA" /> - <include name="IDR_HOTWORD_KEEP_ALIVE_JS" file="hotword/keep_alive.js" type="BINDATA" /> - <include name="IDR_HOTWORD_LAUNCHER_MANAGER_JS" file="hotword/launcher_manager.js" type="BINDATA" /> - <include name="IDR_HOTWORD_LOGGING_JS" file="hotword/logging.js" type="BINDATA" /> - <include name="IDR_HOTWORD_MANAGER_JS" file="hotword/manager.js" type="BINDATA" /> - <include name="IDR_HOTWORD_METRICS_JS" file="hotword/metrics.js" type="BINDATA" /> - <include name="IDR_HOTWORD_NACL_MANAGER_JS" file="hotword/nacl_manager.js" type="BINDATA" /> - <include name="IDR_HOTWORD_PAGE_AUDIO_MANAGER_JS" file="hotword/page_audio_manager.js" type="BINDATA" /> - <include name="IDR_HOTWORD_STATE_MANAGER_JS" file="hotword/state_manager.js" type="BINDATA" /> - <include name="IDR_HOTWORD_TRAINING_MANAGER_JS" file="hotword/training_manager.js" type="BINDATA" /> - </if> <if expr="not is_android"> <include name="IDR_FEEDBACK_DEFAULT_HTML" file="feedback/html/default.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> <include name="IDR_FEEDBACK_SYSINFO_HTML" file="feedback/html/sys_info.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> @@ -137,6 +93,7 @@ <include name="IDR_ARC_SUPPORT_PLAYSTORE_LOGO" file="chromeos/arc_support/icon/playstore.svg" type="BINDATA" /> <include name="IDR_ARC_SUPPORT_PROGRESSBAR_CSS" file="chromeos/arc_support/progressbar.css" type="BINDATA" /> <include name="IDR_ARC_SUPPORT_PROGRESSBAR_JS" file="chromeos/arc_support/progressbar.js" type="BINDATA" /> + <include name="IDR_ARC_SUPPORT_FOCUS_MANAGER_JS" file="chromeos/arc_support/arc_optin_focus_manager.js" type="BINDATA" /> <include name="IDR_ARC_SUPPORT_MAIN" file="chromeos/arc_support/main.html" allowexternalscript="true" type="BINDATA" /> <include name="IDR_ARC_SUPPORT_ICON_48" file="chromeos/arc_support/icon/48.png" type="BINDATA" /> <include name="IDR_ARC_SUPPORT_ICON_96" file="chromeos/arc_support/icon/96.png" type="BINDATA" /> @@ -149,6 +106,7 @@ <include name="IDR_PDF_MAIN_JS" file="pdf/main.js" type="BINDATA" /> <include name="IDR_PDF_PDF_JS" file="pdf/pdf.js" type="BINDATA" /> <include name="IDR_PDF_UI_MANAGER_JS" file="pdf/toolbar_manager.js" type="BINDATA" /> + <include name="IDR_PDF_PDF_FITTING_TYPE_JS" file="pdf/pdf_fitting_type.js" type="BINDATA" /> <include name="IDR_PDF_VIEWPORT_JS" file="pdf/viewport.js" type="BINDATA" /> <include name="IDR_PDF_OPEN_PDF_PARAMS_PARSER_JS" file="pdf/open_pdf_params_parser.js" type="BINDATA" /> <include name="IDR_PDF_NAVIGATOR_JS" file="pdf/navigator.js" type="BINDATA" /> diff --git a/chromium/chrome/browser/resources/discards/.eslintrc.js b/chromium/chrome/browser/resources/discards/.eslintrc.js new file mode 100644 index 00000000000..847d6e99509 --- /dev/null +++ b/chromium/chrome/browser/resources/discards/.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/discards/OWNERS b/chromium/chrome/browser/resources/discards/OWNERS new file mode 100644 index 00000000000..dcd54320481 --- /dev/null +++ b/chromium/chrome/browser/resources/discards/OWNERS @@ -0,0 +1 @@ +file://services/resource_coordinator/OWNERS diff --git a/chromium/chrome/browser/resources/discards/discards.css b/chromium/chrome/browser/resources/discards/discards.css new file mode 100644 index 00000000000..b5a965016cc --- /dev/null +++ b/chromium/chrome/browser/resources/discards/discards.css @@ -0,0 +1,84 @@ +/* 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. */ + +table { + border-collapse: collapse; +} + +table td, +table th { + border: 1px solid #777; + padding-left: 4px; + padding-right: 4px; +} + +table th { + -webkit-padding-end: 16px; + background: rgb(224, 236, 255); + cursor: pointer; + padding-bottom: 4px; + padding-top: 4px; + white-space: nowrap; +} + +table td.title-cell { + max-width: 200px; + overflow: hidden; + white-space: nowrap; +} + +table td div.title-cell-container { + align-items: center; + display: flex; + justify-content: flex-start; +} + +table td div.favicon-div { + height: 16px; + margin: 3px; + padding: 0; + width: 16px; +} + +table td div.favicon-div img { + height: 16px; + width: 16px; +} + +table td div.title-div { + margin: 0; + overflow: hidden; + padding: 0; + white-space: nowrap; +} + +table td.tab-url-cell { + max-width: 200px; + overflow: hidden; + white-space: nowrap; +} + +table td.boolean-cell, +table td.discard-count-cell { + text-align: center; +} + +table td div.is-auto-discardable-link, +table td.discard-links-cell { + font-size: 0.6rem; +} + +table tr:hover { + background: rgb(255, 255, 187); +} + +th.sort-column::after { + content: '▲'; + position: absolute; +} + +th[data-sort-reverse].sort-column::after { + content: '▼'; + position: absolute; +} diff --git a/chromium/chrome/browser/resources/discards/discards.html b/chromium/chrome/browser/resources/discards/discards.html new file mode 100644 index 00000000000..aaa2930665a --- /dev/null +++ b/chromium/chrome/browser/resources/discards/discards.html @@ -0,0 +1,84 @@ +<!-- +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. + +This is an internal only page meant for debugging. It is not intended for +general use and is not localized. +--> +<!doctype html> +<html lang="en"> + <head> + <title>Discards</title> + <meta charset="utf-8"> + <link rel="stylesheet" href="chrome://resources/css/action_link.css"> + <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> + <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/discards/discards.mojom.js"></script> + <script src="discards.js"></script> + <link rel="stylesheet" type="text/css" href="discards.css"> + </head> + <body> + <h1>Discards</h1> + <div is="action-link" id="discard-now-link"> + [Discard a tab now] + </div> + <div is="action-link" id="discard-now-urgent-link"> + [Urgent discard a tab now] + </div> + <table id="tab-discard-info-table"> + <thead> + <tr id="tab-discards-info-table-header"> + <th data-sort-key="utilityRank" class="sort-column">Utility Rank</th> + <th data-sort-key="title">Tab Title</th> + <th data-sort-key="tabUrl">Tab URL</th> + <th data-sort-key="isApp">App</th> + <th data-sort-key="isInternal">Internal</th> + <th data-sort-key="isMedia">Media</th> + <th data-sort-key="isPinned">Pinned</th> + <th data-sort-key="isDiscarded">Discarded</th> + <th data-sort-key="discardCount">Discard Count</th> + <th data-sort-key="isAutoDiscardable">Auto Discardable</th> + <th data-sort-key="lastActiveSeconds">Last Active</th> + </tr> + </thead> + <tbody id="tab-discards-info-table-body"> + </tbody> + </table> + <template id="tab-discard-info-row"> + <tr> + <td class="utility-rank-cell"></td> + <td class="title-cell"> + <div class="title-cell-container"> + <div class="favicon-div"> + <img class="favicon" alt="FavIcon"> + </div> + <div class="title-div"></div> + </div> + </td> + <td class="tab-url-cell"></td> + <td class="is-app-cell boolean-cell"></td> + <td class="is-internal-cell boolean-cell"></td> + <td class="is-media-cell boolean-cell"></td> + <td class="is-pinned-cell boolean-cell"></td> + <td class="is-discarded-cell boolean-cell"></td> + <td class="discard-count-cell"></td> + <td class="is-auto-discardable-cell boolean-cell"> + <div class="is-auto-discardable-div"></div> + <div is="action-link" class="is-auto-discardable-link"> + Toggle + </div> + </td> + <td class="last-active-cell"></td> + <td class="discard-links-cell"> + <div is="action-link" class="discard-link">[Discard]</div> + <div is="action-link" class="discard-urgent-link"> + [Urgent Discard] + </div> + </td> + </tr> + </template> + </body> +</html> diff --git a/chromium/chrome/browser/resources/discards/discards.js b/chromium/chrome/browser/resources/discards/discards.js new file mode 100644 index 00000000000..210abe907a5 --- /dev/null +++ b/chromium/chrome/browser/resources/discards/discards.js @@ -0,0 +1,433 @@ +// 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('discards', function() { + 'use strict'; + + // The following variables are initialized by 'initialize'. + // Points to the Mojo WebUI handler. + let uiHandler; + // After initialization this points to the discard info table body. + let tabDiscardsInfoTableBody; + // This holds the sorted tab discard infos as retrieved from the uiHandler. + let infos; + // Holds information about the current sorting of the table. + let sortKey; + let sortReverse; + // Points to the timer that refreshes the table content. + let updateTimer; + + // Specifies the update interval of the page, in ms. + const UPDATE_INTERVAL_MS = 1000; + + /** + * Ensures the discards info table has the appropriate length. Decorates + * newly created rows with a 'row-index' attribute to enable event listeners + * to quickly determine the index of the row. + */ + function ensureTabDiscardsInfoTableLength() { + let rows = tabDiscardsInfoTableBody.querySelectorAll('tr'); + if (rows.length < infos.length) { + for (let i = rows.length; i < infos.length; ++i) { + let row = createEmptyTabDiscardsInfoTableRow(); + row.setAttribute('data-row-index', i.toString()); + tabDiscardsInfoTableBody.appendChild(row); + } + } else if (rows.length > infos.length) { + for (let i = infos.length; i < rows.length; ++i) { + tabDiscardsInfoTableBody.removeChild(rows[i]); + } + } + } + + /** + * Compares two TabDiscardsInfos based on the data in the provided sort-key. + * @param {string} sortKey The key of the sort. See the "data-sort-key" + * attribute of the table headers for valid sort-keys. + * @param {boolean|number|string} a The first value being compared. + * @param {boolean|number|string} b The second value being compared. + * @return {number} A negative number if a < b, 0 if a == b, and a positive + * number if a > b. + */ + function compareTabDiscardsInfos(sortKey, a, b) { + let val1 = a[sortKey]; + let val2 = b[sortKey]; + + // Compares strings. + if (sortKey == 'title' || sortKey == 'tabUrl') { + val1 = val1.toLowerCase(); + val2 = val2.toLowerCase(); + if (val1 == val2) + return 0; + return val1 > val2 ? 1 : -1; + } + + // Compares boolean fields. + if ([ + 'isApp', 'isInternal', 'isMedia', 'isPinned', 'isDiscarded', + 'isAutoDiscardable' + ].includes(sortKey)) { + if (val1 == val2) + return 0; + return val1 ? 1 : -1; + } + + // Compares numeric fields. + if (['discardCount', 'utilityRank', 'lastActiveSeconds'].includes( + sortKey)) { + return val1 - val2; + } + + assertNotReached('Unsupported sort key: ' + sortKey); + return 0; + } + + /** + * Sorts the tab discards info data in |infos| according to the current + * |sortKey|. + */ + function sortTabDiscardsInfoTable() { + infos = infos.sort((a, b) => { + return (sortReverse ? -1 : 1) * compareTabDiscardsInfos(sortKey, a, b); + }); + } + + /** + * Pluralizes a string according to the given count. Assumes that appending an + * 's' is sufficient to make a string plural. + * @param {string} s The string to be made plural if necessary. + * @param {number} n The count of the number of ojects. + * @return {string} The plural version of |s| if n != 1, otherwise |s|. + */ + function maybeMakePlural(s, n) { + return n == 1 ? s : s + 's'; + } + + /** + * Converts a |secondsAgo| last-active time to a user friendly string. + * @param {number} secondsAgo The amount of time since the tab was active. + * @return {string} An English string representing the last active time. + */ + function lastActiveToString(secondsAgo) { + // These constants aren't perfect, but close enough. + const SECONDS_PER_MINUTE = 60; + const MINUTES_PER_HOUR = 60; + const SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR; + const HOURS_PER_DAY = 24; + const SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; + const DAYS_PER_WEEK = 7; + const SECONDS_PER_WEEK = SECONDS_PER_DAY * DAYS_PER_WEEK; + const SECONDS_PER_MONTH = SECONDS_PER_DAY * 30.5; + const SECONDS_PER_YEAR = SECONDS_PER_DAY * 365; + + // Seconds ago. + if (secondsAgo < SECONDS_PER_MINUTE) + return 'just now'; + + // Minutes ago. + let minutesAgo = Math.floor(secondsAgo / SECONDS_PER_MINUTE); + if (minutesAgo < MINUTES_PER_HOUR) { + return minutesAgo.toString() + maybeMakePlural(' minute', minutesAgo) + + ' ago'; + } + + // Hours and minutes and ago. + let hoursAgo = Math.floor(secondsAgo / SECONDS_PER_HOUR); + minutesAgo = minutesAgo % MINUTES_PER_HOUR; + if (hoursAgo < HOURS_PER_DAY) { + let s = hoursAgo.toString() + maybeMakePlural(' hour', hoursAgo); + if (minutesAgo > 0) { + s += ' and ' + minutesAgo.toString() + + maybeMakePlural(' minute', minutesAgo); + } + s += ' ago'; + return s; + } + + // Days ago. + let daysAgo = Math.floor(secondsAgo / SECONDS_PER_DAY); + if (daysAgo < DAYS_PER_WEEK) { + return daysAgo.toString() + maybeMakePlural(' day', daysAgo) + ' ago'; + } + + // Weeks ago. There's an awkward gap to bridge where 4 weeks can have + // elapsed but not quite 1 month. Be sure to use weeks to report that. + let weeksAgo = Math.floor(secondsAgo / SECONDS_PER_WEEK); + let monthsAgo = Math.floor(secondsAgo / SECONDS_PER_MONTH); + if (monthsAgo < 1) { + return 'over ' + weeksAgo.toString() + + maybeMakePlural(' week', weeksAgo) + ' ago'; + } + + // Months ago. + let yearsAgo = Math.floor(secondsAgo / SECONDS_PER_YEAR); + if (yearsAgo < 1) { + return 'over ' + monthsAgo.toString() + + maybeMakePlural(' month', monthsAgo) + ' ago'; + } + + // Years ago. + return 'over ' + yearsAgo.toString() + maybeMakePlural(' year', yearsAgo) + + ' ago'; + } + + /** + * Returns a string representation of a boolean value for display in a table. + * @param {boolean} bool A boolean value. + * @return {string} A string representing the bool. + */ + function boolToString(bool) { + return bool ? '✔' : '\xa0'; + } + + /** + * Returns the index of the row in the table that houses the given |element|. + * @param {HTMLElement} element Any element in the DOM. + */ + function getRowIndex(element) { + let row = element.closest('tr'); + return parseInt(row.getAttribute('data-row-index'), 10); + } + + /** + * Creates an empty tab discards table row with action-link listeners, etc. + * By default the links are inactive. + */ + function createEmptyTabDiscardsInfoTableRow() { + let template = $('tab-discard-info-row'); + let content = document.importNode(template.content, true); + let row = content.querySelector('tr'); + + // Set up the listener for the auto-discardable toggle action. + let isAutoDiscardable = row.querySelector('.is-auto-discardable-link'); + isAutoDiscardable.setAttribute('disabled', ''); + isAutoDiscardable.addEventListener('click', (e) => { + // Get the info backing this row. + let info = infos[getRowIndex(e.target)]; + // Disable the action. The update function is responsible for + // re-enabling actions if necessary. + e.target.setAttribute('disabled', ''); + // Perform the action. + uiHandler.setAutoDiscardable(info.id, !info.isAutoDiscardable) + .then(stableUpdateTabDiscardsInfoTable()); + }); + + // Set up the listeners for discard links. + let discardListener = function(e) { + // Get the info backing this row. + let info = infos[getRowIndex(e.target)]; + // Determine whether this is urgent or not. + let urgent = e.target.classList.contains('discard-urgent-link'); + // Disable the action. The update function is responsible for + // re-enabling actions if necessary. + e.target.setAttribute('disabled', ''); + // Perform the action. + uiHandler.discardById(info.id, urgent).then((response) => { + stableUpdateTabDiscardsInfoTable(); + }); + }; + let discardLink = row.querySelector('.discard-link'); + let discardUrgentLink = row.querySelector('.discard-urgent-link'); + discardLink.addEventListener('click', discardListener); + discardUrgentLink.addEventListener('click', discardListener); + + return row; + } + + /** + * Updates a tab discards info table row in place. Sets/unsets 'disabled' + * attributes on action-links as necessary, and populates all contents. + */ + function updateTabDiscardsInfoTableRow(row, info) { + // Update the content. + row.querySelector('.utility-rank-cell').textContent = + info.utilityRank.toString(); + row.querySelector('.favicon').src = + info.faviconUrl ? info.faviconUrl : 'chrome://favicon'; + row.querySelector('.title-div').textContent = info.title; + row.querySelector('.tab-url-cell').textContent = info.tabUrl; + row.querySelector('.is-app-cell').textContent = boolToString(info.isApp); + row.querySelector('.is-internal-cell').textContent = + boolToString(info.isInternal); + row.querySelector('.is-media-cell').textContent = + boolToString(info.isMedia); + row.querySelector('.is-pinned-cell').textContent = + boolToString(info.isPinned); + row.querySelector('.is-discarded-cell').textContent = + boolToString(info.isDiscarded); + row.querySelector('.discard-count-cell').textContent = + info.discardCount.toString(); + row.querySelector('.is-auto-discardable-div').textContent = + boolToString(info.isAutoDiscardable); + row.querySelector('.last-active-cell').textContent = + lastActiveToString(info.lastActiveSeconds); + + // Enable/disable action links as appropriate. + row.querySelector('.is-auto-discardable-link').removeAttribute('disabled'); + let discardLink = row.querySelector('.discard-link'); + let discardUrgentLink = row.querySelector('.discard-urgent-link'); + if (info.isDiscarded) { + discardLink.setAttribute('disabled', ''); + discardUrgentLink.setAttribute('disabled', ''); + } else { + discardLink.removeAttribute('disabled'); + discardUrgentLink.removeAttribute('disabled'); + } + } + + /** + * Causes the discards info table to be rendered. Reuses existing table rows + * in place to minimize disruption to the page. + */ + function renderTabDiscardsInfoTable() { + ensureTabDiscardsInfoTableLength(); + let rows = tabDiscardsInfoTableBody.querySelectorAll('tr'); + for (let i = 0; i < infos.length; ++i) + updateTabDiscardsInfoTableRow(rows[i], infos[i]); + } + + /** + * Causes the discard info table to be updated in as stable a manner as + * possible. That is, rows will stay in their relative positions, even if the + * current sort order is violated. Only the addition or removal of rows (tabs) + * can cause the layout to change. + */ + function stableUpdateTabDiscardsInfoTableImpl() { + uiHandler.getTabDiscardsInfo().then((response) => { + let newInfos = response.infos; + let stableInfos = []; + + // Update existing infos in place, remove old ones, and append new ones. + // This tries to keep the existing ordering stable so that clicking links + // is minimally disruptive. + for (let i = 0; i < infos.length; ++i) { + let oldInfo = infos[i]; + let newInfo = null; + for (let j = 0; j < newInfos.length; ++j) { + if (newInfos[j].id == oldInfo.id) { + newInfo = newInfos[j]; + break; + } + } + + // Old infos that have corresponding new infos are pushed first, in the + // current order of the old infos. + if (newInfo != null) + stableInfos.push(newInfo); + } + + // Make sure info about new tabs is appended to the end, in the order they + // were originally returned. + for (let i = 0; i < newInfos.length; ++i) { + let newInfo = newInfos[i]; + let oldInfo = null; + for (let j = 0; j < infos.length; ++j) { + if (infos[j].id == newInfo.id) { + oldInfo = infos[j]; + break; + } + } + + // Entirely new information (has no corresponding old info) is appended + // to the end. + if (oldInfo == null) + stableInfos.push(newInfo); + } + + // Swap out the current info with the new stably sorted information. + infos = stableInfos; + + // Render the content in place. + renderTabDiscardsInfoTable(); + }); + } + + /** + * A wrapper to stableUpdateTabDiscardsInfoTableImpl that is called due to + * user action and not due to the automatic timer. Cancels the existing timer + * and reschedules it after rendering instantaneously. + */ + function stableUpdateTabDiscardsInfoTable() { + if (updateTimer) + clearInterval(updateTimer); + stableUpdateTabDiscardsInfoTableImpl(); + updateTimer = + setInterval(stableUpdateTabDiscardsInfoTableImpl, UPDATE_INTERVAL_MS); + } + + /** + * Initializes this page. Invoked by the DOMContentLoaded event. + */ + function initialize() { + uiHandler = new mojom.DiscardsDetailsProviderPtr; + Mojo.bindInterface( + mojom.DiscardsDetailsProvider.name, mojo.makeRequest(uiHandler).handle); + + tabDiscardsInfoTableBody = $('tab-discards-info-table-body'); + infos = []; + sortKey = 'utilityRank'; + sortReverse = false; + updateTimer = null; + + // Set the column sort handlers. + let tabDiscardsInfoTableHeader = $('tab-discards-info-table-header'); + let headers = tabDiscardsInfoTableHeader.children; + for (let header of headers) { + header.addEventListener('click', (e) => { + let newSortKey = e.target.dataset.sortKey; + + // Skip columns that aren't explicitly labeled with a sort-key + // attribute. + if (newSortKey == null) + return; + + // Reverse the sort key if the key itself hasn't changed. + if (sortKey == newSortKey) { + sortReverse = !sortReverse; + } else { + sortKey = newSortKey; + sortReverse = false; + } + + // Undecorate the old sort column, and decorate the new one. + let oldSortColumn = document.querySelector('.sort-column'); + oldSortColumn.classList.remove('sort-column'); + e.target.classList.add('sort-column'); + if (sortReverse) + e.target.setAttribute('data-sort-reverse', ''); + else + e.target.removeAttribute('data-sort-reverse'); + + sortTabDiscardsInfoTable(); + renderTabDiscardsInfoTable(); + }); + } + + // Setup the "Discard a tab now" links. + let discardNow = $('discard-now-link'); + let discardNowUrgent = $('discard-now-urgent-link'); + let discardListener = function(e) { + e.target.setAttribute('disabled', ''); + let urgent = e.target.id.includes('urgent'); + uiHandler.discard(urgent).then(() => { + stableUpdateTabDiscardsInfoTable(); + e.target.removeAttribute('disabled'); + }); + }; + discardNow.addEventListener('click', discardListener); + discardNowUrgent.addEventListener('click', discardListener); + + stableUpdateTabDiscardsInfoTable(); + } + + document.addEventListener('DOMContentLoaded', initialize); + + // These functions are exposed on the 'discards' object created by + // cr.define. This allows unittesting of these functions. + return { + compareTabDiscardsInfos: compareTabDiscardsInfos, + lastActiveToString: lastActiveToString, + maybeMakePlural: maybeMakePlural + }; +}); diff --git a/chromium/chrome/browser/resources/download_internals/download_internals.css b/chromium/chrome/browser/resources/download_internals/download_internals.css index 63dab4dbfc0..bc909013457 100644 --- a/chromium/chrome/browser/resources/download_internals/download_internals.css +++ b/chromium/chrome/browser/resources/download_internals/download_internals.css @@ -12,6 +12,10 @@ h1 { font-size: 15px; } +.sub-text { + font-size: 10px; +} + .service-entry-new { background-color: rgb(242, 242, 242) } diff --git a/chromium/chrome/browser/resources/download_internals/download_internals.html b/chromium/chrome/browser/resources/download_internals/download_internals.html index 259ed42fc33..49e67cdce47 100644 --- a/chromium/chrome/browser/resources/download_internals/download_internals.html +++ b/chromium/chrome/browser/resources/download_internals/download_internals.html @@ -29,6 +29,10 @@ File Monitor: <span id="service-status-file" class="status"></span> </div> <h4>Entry Requests</h4> + <div class="sub-text"> + The entry URLs have been modified to strip query params so that possibly + private information is hidden from view. + </div> <div id="download-service-request-info"> <table class="styled-table"> <thead> diff --git a/chromium/chrome/browser/resources/extensions/extensions.js b/chromium/chrome/browser/resources/extensions/extensions.js index d46ea50ad19..663f8382b3d 100644 --- a/chromium/chrome/browser/resources/extensions/extensions.js +++ b/chromium/chrome/browser/resources/extensions/extensions.js @@ -108,7 +108,7 @@ cr.define('extensions', function() { var dragTarget = document.documentElement; /** @private {extensions.DragAndDropHandler} */ this.dragWrapperHandler_ = - new extensions.DragAndDropHandler(true, dragTarget); + new extensions.DragAndDropHandler(true, false, dragTarget); dragTarget.addEventListener('extension-drag-started', function() { ExtensionSettings.showOverlay($('drop-target-overlay')); }); diff --git a/chromium/chrome/browser/resources/feedback/js/feedback.js b/chromium/chrome/browser/resources/feedback/js/feedback.js index 62712dab768..6089c1b9c55 100644 --- a/chromium/chrome/browser/resources/feedback/js/feedback.js +++ b/chromium/chrome/browser/resources/feedback/js/feedback.js @@ -215,21 +215,6 @@ function cancel(e) { scheduleWindowClose(); } -/** - * Converts a blob data URL to a blob object. - * @param {string} url The data URL to convert. - * @return {Blob} Blob object containing the data. - */ -function dataUrlToBlob(url) { - var mimeString = url.split(',')[0].split(':')[1].split(';')[0]; - var data = atob(url.split(',')[1]); - var dataArray = []; - for (var i = 0; i < data.length; ++i) - dataArray.push(data.charCodeAt(i)); - - return new Blob([new Uint8Array(dataArray)], {type: mimeString}); -} - // <if expr="chromeos"> /** * Update the page when performance feedback state is changed. @@ -347,17 +332,17 @@ 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', - $('screenshot-image').width > MAX_SCREENSHOT_WIDTH); - feedbackInfo.screenshot = dataUrlToBlob(screenshotDataUrl); + screenshotCanvas.toBlob(function(blob) { + $('screenshot-image').src = URL.createObjectURL(blob); + // 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').alt = 'screenshot'; + $('screenshot-image') + .classList.toggle( + 'wide-screen', + $('screenshot-image').width > MAX_SCREENSHOT_WIDTH); + feedbackInfo.screenshot = blob; + }); }); chrome.feedbackPrivate.getUserEmail(function(email) { diff --git a/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js b/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js index fa1e41103f4..f55f6593447 100644 --- a/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js +++ b/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js @@ -3,6 +3,7 @@ // found in the LICENSE file. // <include src="saml_handler.js"> +// Note: webview_event_manager.js is already included by saml_handler.js. /** * @fileoverview An UI component to authenciate to Chrome. The component hosts @@ -122,9 +123,6 @@ cr.define('cr.login', function() { * @constructor */ function Authenticator(webview) { - this.webview_ = typeof webview == 'string' ? $(webview) : webview; - assert(this.webview_); - this.isLoaded_ = false; this.email_ = null; this.password_ = null; @@ -145,38 +143,19 @@ cr.define('cr.login', function() { this.gapsCookieSent_ = false; this.newGapsCookie_ = null; this.readyFired_ = false; + this.webviewEventManager_ = WebviewEventManager.create(); this.clientId_ = null; - this.samlHandler_ = new cr.login.SamlHandler(this.webview_); this.confirmPasswordCallback = null; this.noPasswordCallback = null; this.insecureContentBlockedCallback = null; this.samlApiUsedCallback = null; this.missingGaiaInfoCallback = null; this.needPassword = true; - this.samlHandler_.addEventListener( - 'insecureContentBlocked', this.onInsecureContentBlocked_.bind(this)); - this.samlHandler_.addEventListener( - 'authPageLoaded', this.onAuthPageLoaded_.bind(this)); - this.samlHandler_.addEventListener( - 'videoEnabled', this.onVideoEnabled_.bind(this)); - this.samlHandler_.addEventListener( - 'apiPasswordAdded', this.onSamlApiPasswordAdded_.bind(this)); - - this.webview_.addEventListener('droplink', this.onDropLink_.bind(this)); - this.webview_.addEventListener('newwindow', this.onNewWindow_.bind(this)); - this.webview_.addEventListener( - 'contentload', this.onContentLoad_.bind(this)); - this.webview_.addEventListener('loadabort', this.onLoadAbort_.bind(this)); - this.webview_.addEventListener('loadcommit', this.onLoadCommit_.bind(this)); - this.webview_.request.onCompleted.addListener( - this.onRequestCompleted_.bind(this), - {urls: ['<all_urls>'], types: ['main_frame']}, ['responseHeaders']); - this.webview_.request.onHeadersReceived.addListener( - this.onHeadersReceived_.bind(this), - {urls: ['<all_urls>'], types: ['main_frame', 'xmlhttprequest']}, - ['responseHeaders']); + + this.bindToWebview_(webview); + window.addEventListener( 'message', this.onMessageFromWebview_.bind(this), false); window.addEventListener('focus', this.onFocus_.bind(this), false); @@ -217,6 +196,74 @@ cr.define('cr.login', function() { }; /** + * Binds this authenticator to the passed webview. + * @param {!Object} webview the new webview to be used by this Authenticator. + * @private + */ + Authenticator.prototype.bindToWebview_ = function(webview) { + assert(!this.webview_); + assert(!this.samlHandler_); + + this.webview_ = typeof webview == 'string' ? $(webview) : webview; + + this.samlHandler_ = new cr.login.SamlHandler(this.webview_); + this.webviewEventManager_.addEventListener( + this.samlHandler_, 'insecureContentBlocked', + this.onInsecureContentBlocked_.bind(this)); + this.webviewEventManager_.addEventListener( + this.samlHandler_, 'authPageLoaded', this.onAuthPageLoaded_.bind(this)); + this.webviewEventManager_.addEventListener( + this.samlHandler_, 'videoEnabled', this.onVideoEnabled_.bind(this)); + this.webviewEventManager_.addEventListener( + this.samlHandler_, 'apiPasswordAdded', + this.onSamlApiPasswordAdded_.bind(this)); + + this.webviewEventManager_.addEventListener( + this.webview_, 'droplink', this.onDropLink_.bind(this)); + this.webviewEventManager_.addEventListener( + this.webview_, 'newwindow', this.onNewWindow_.bind(this)); + this.webviewEventManager_.addEventListener( + this.webview_, 'contentload', this.onContentLoad_.bind(this)); + this.webviewEventManager_.addEventListener( + this.webview_, 'loadabort', this.onLoadAbort_.bind(this)); + this.webviewEventManager_.addEventListener( + this.webview_, 'loadcommit', this.onLoadCommit_.bind(this)); + + this.webviewEventManager_.addWebRequestEventListener( + this.webview_.request.onCompleted, this.onRequestCompleted_.bind(this), + {urls: ['<all_urls>'], types: ['main_frame']}, ['responseHeaders']); + this.webviewEventManager_.addWebRequestEventListener( + this.webview_.request.onHeadersReceived, + this.onHeadersReceived_.bind(this), + {urls: ['<all_urls>'], types: ['main_frame', 'xmlhttprequest']}, + ['responseHeaders']); + }; + + /** + * Unbinds this Authenticator from the currently bound webview. + * @private + */ + Authenticator.prototype.unbindFromWebview_ = function() { + assert(this.webview_); + assert(this.samlHandler_); + + this.webviewEventManager_.removeAllListeners(); + + this.webview_ = undefined; + this.samlHandler_.unbindFromWebview(); + this.samlHandler_ = undefined; + }; + + /** + * Re-binds to another webview. + * @param {Object} webview the new webview to be used by this Authenticator. + */ + Authenticator.prototype.rebindWebview = function(webview) { + this.unbindFromWebview_(); + this.bindToWebview_(webview); + }; + + /** * Loads the authenticator component with the given parameters. * @param {AuthMode} authMode Authorization mode. * @param {Object} data Parameters for the authorization flow. diff --git a/chromium/chrome/browser/resources/gaia_auth_host/saml_handler.js b/chromium/chrome/browser/resources/gaia_auth_host/saml_handler.js index 89907937681..670bf7f90af 100644 --- a/chromium/chrome/browser/resources/gaia_auth_host/saml_handler.js +++ b/chromium/chrome/browser/resources/gaia_auth_host/saml_handler.js @@ -3,6 +3,7 @@ // found in the LICENSE file. // <include src="post_message_channel.js"> +// <include src="webview_event_manager.js"> /** * @fileoverview Saml support for webview based auth. @@ -34,6 +35,9 @@ cr.define('cr.login', function() { /** @const */ var SAML_HEADER = 'google-accounts-saml'; + /** @const */ + var injectedScriptName = 'samlInjected'; + /** * The script to inject into webview and its sub frames. * @type {string} @@ -141,23 +145,30 @@ cr.define('cr.login', function() { */ this.blockInsecureContent = false; - this.webview_.addEventListener( - 'contentload', this.onContentLoad_.bind(this)); - this.webview_.addEventListener('loadabort', this.onLoadAbort_.bind(this)); - this.webview_.addEventListener('loadcommit', this.onLoadCommit_.bind(this)); - this.webview_.addEventListener( - 'permissionrequest', this.onPermissionRequest_.bind(this)); + this.webviewEventManager_ = WebviewEventManager.create(); - this.webview_.request.onBeforeRequest.addListener( + this.webviewEventManager_.addEventListener( + this.webview_, 'contentload', this.onContentLoad_.bind(this)); + this.webviewEventManager_.addEventListener( + this.webview_, 'loadabort', this.onLoadAbort_.bind(this)); + this.webviewEventManager_.addEventListener( + this.webview_, 'loadcommit', this.onLoadCommit_.bind(this)); + this.webviewEventManager_.addEventListener( + this.webview_, 'permissionrequest', + this.onPermissionRequest_.bind(this)); + + this.webviewEventManager_.addWebRequestEventListener( + this.webview_.request.onBeforeRequest, this.onInsecureRequest.bind(this), {urls: ['http://*/*', 'file://*/*', 'ftp://*/*']}, ['blocking']); - this.webview_.request.onHeadersReceived.addListener( + this.webviewEventManager_.addWebRequestEventListener( + this.webview_.request.onHeadersReceived, this.onHeadersReceived_.bind(this), {urls: ['<all_urls>'], types: ['main_frame', 'xmlhttprequest']}, ['blocking', 'responseHeaders']); this.webview_.addContentScripts([{ - name: 'samlInjected', + name: injectedScriptName, matches: ['http://*/*', 'https://*/*'], js: {code: injectedJs}, all_frames: true, @@ -217,6 +228,16 @@ cr.define('cr.login', function() { }, /** + * Removes the injected content script and unbinds all listeners from the + * webview passed to the constructor. This SAMLHandler will be unusable + * after this function returns. + */ + unbindFromWebview: function() { + this.webview_.removeContentScripts([injectedScriptName]); + this.webviewEventManager_.removeAllListeners(); + }, + + /** * Resets all auth states */ reset: function() { diff --git a/chromium/chrome/browser/resources/gaia_auth_host/webview_event_manager.js b/chromium/chrome/browser/resources/gaia_auth_host/webview_event_manager.js new file mode 100644 index 00000000000..527ed101de2 --- /dev/null +++ b/chromium/chrome/browser/resources/gaia_auth_host/webview_event_manager.js @@ -0,0 +1,66 @@ +// 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 + * Provides WebviewEventManager which can register and keep track of listeners + * on EventTargets and WebRequests, and unregister all listeners later. + */ +'use strict'; + +/** + * Creates a new WebviewEventManager. + */ +function WebviewEventManager() { + this.unbindWebviewCleanupFunctions_ = []; +} + +WebviewEventManager.prototype = { + /** + * Adds a EventListener to |eventTarget| and adds a clean-up function so we + * can remove the listener in unbindFromWebview. + * @param {Object} webview the object to add the listener to + * @param {string} type the event type + * @param {Function} listener the event listener + * @private + */ + addEventListener: function(eventTarget, type, listener) { + eventTarget.addEventListener(type, listener); + this.unbindWebviewCleanupFunctions_.push( + eventTarget.removeEventListener.bind(eventTarget, type, listener)); + }, + + /** + * Adds a listener to |webRequestEvent| and adds a clean-up function so we can + * remove the listener in unbindFromWebview. + * @param {Object} webRequestEvent the object to add the listener to + * @param {string} type the event type + * @param {Function} listener the event listener + * @private + */ + addWebRequestEventListener: function( + webRequestEvent, listener, filter, extraInfoSpec) { + webRequestEvent.addListener(listener, filter, extraInfoSpec); + this.unbindWebviewCleanupFunctions_.push( + webRequestEvent.removeListener.bind(webRequestEvent, listener)); + }, + + /** + * Unbinds this Authenticator from the currently bound webview. + * @private + */ + removeAllListeners: function() { + for (var i = 0; i < this.unbindWebviewCleanupFunctions_.length; i++) + this.unbindWebviewCleanupFunctions_[i](); + this.unbindWebviewCleanupFunctions_ = []; + } +}; + +/** + * Class factory. + * @return {WebviewEventManager} + */ +WebviewEventManager.create = function() { + return new WebviewEventManager(); +}; diff --git a/chromium/chrome/browser/resources/hangout_services/manifest.json b/chromium/chrome/browser/resources/hangout_services/manifest.json index af76f3d202c..0c062af714f 100644 --- a/chromium/chrome/browser/resources/hangout_services/manifest.json +++ b/chromium/chrome/browser/resources/hangout_services/manifest.json @@ -5,7 +5,7 @@ "name": "Google Hangouts", // Note: Always update the version number when this file is updated. Chrome // triggers extension preferences update on the version increase. - "version": "1.3.5", + "version": "1.3.6", "manifest_version": 2, "externally_connectable": { "matches": [ diff --git a/chromium/chrome/browser/resources/hangout_services/thunk.js b/chromium/chrome/browser/resources/hangout_services/thunk.js index 85a7e688a8c..4e9fe41578c 100644 --- a/chromium/chrome/browser/resources/hangout_services/thunk.js +++ b/chromium/chrome/browser/resources/hangout_services/thunk.js @@ -191,9 +191,8 @@ chrome.runtime.onMessageExternal.addListener(function( } else if (method == 'setAudioExperiments') { var experiments = message['experiments']; chrome.webrtcAudioPrivate.setAudioExperiments( - requestInfo, origin, experiments); - doSendResponse(); - return false; + requestInfo, origin, experiments, doSendResponse); + return true; } throw new Error('Unknown method: ' + method); diff --git a/chromium/chrome/browser/resources/hotword/OWNERS b/chromium/chrome/browser/resources/hotword/OWNERS deleted file mode 100644 index 2ae98b3a1f1..00000000000 --- a/chromium/chrome/browser/resources/hotword/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -kcarattini@chromium.org -mgiuca@chromium.org diff --git a/chromium/chrome/browser/resources/hotword/always_on_manager.js b/chromium/chrome/browser/resources/hotword/always_on_manager.js deleted file mode 100644 index f34bc6da370..00000000000 --- a/chromium/chrome/browser/resources/hotword/always_on_manager.js +++ /dev/null @@ -1,38 +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('hotword', function() { - 'use strict'; - - /** - * Class used to manage always-on hotwording. Automatically starts hotwording - * on startup, if always-on is enabled, and starts/stops hotwording at - * appropriate times. - * @param {!hotword.StateManager} stateManager - * @constructor - * @extends {hotword.BaseSessionManager} - */ - function AlwaysOnManager(stateManager) { - hotword.BaseSessionManager.call( - this, stateManager, hotword.constants.SessionSource.ALWAYS); - } - - AlwaysOnManager.prototype = { - __proto__: hotword.BaseSessionManager.prototype, - - /** @override */ - enabled: function() { - return this.stateManager.isAlwaysOnEnabled(); - }, - - /** @override */ - updateListeners: function() { - hotword.BaseSessionManager.prototype.updateListeners.call(this); - if (this.enabled()) - this.startSession(); - } - }; - - return {AlwaysOnManager: AlwaysOnManager}; -}); diff --git a/chromium/chrome/browser/resources/hotword/audio_client.js b/chromium/chrome/browser/resources/hotword/audio_client.js deleted file mode 100644 index 552dd49cdaf..00000000000 --- a/chromium/chrome/browser/resources/hotword/audio_client.js +++ /dev/null @@ -1,375 +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. - -'use strict'; - -/* eslint-disable no-restricted-properties */ - -/** - * @fileoverview This is the audio client content script injected into eligible - * Google.com and New tab pages for interaction between the Webpage and the - * Hotword extension. - */ - -(function() { -/** - * @constructor - */ -var AudioClient = function() { - /** @private {Element} */ - this.speechOverlay_ = null; - - /** @private {number} */ - this.checkSpeechUiRetries_ = 0; - - /** - * Port used to communicate with the audio manager. - * @private {?Port} - */ - this.port_ = null; - - /** - * Keeps track of the effects of different commands. Used to verify that - * proper UIs are shown to the user. - * @private {Object<AudioClient.CommandToPage, Object>} - */ - this.uiStatus_ = null; - - /** - * Bound function used to handle commands sent from the page to this script. - * @private {Function} - */ - this.handleCommandFromPageFunc_ = null; -}; - -/** - * Messages sent to the page to control the voice search UI. - * @enum {string} - */ -AudioClient.CommandToPage = { - HOTWORD_VOICE_TRIGGER: 'vt', - HOTWORD_STARTED: 'hs', - HOTWORD_ENDED: 'hd', - HOTWORD_TIMEOUT: 'ht', - HOTWORD_ERROR: 'he' -}; - -/** - * Messages received from the page used to indicate voice search state. - * @enum {string} - */ -AudioClient.CommandFromPage = { - SPEECH_START: 'ss', - SPEECH_END: 'se', - SPEECH_RESET: 'sr', - SHOWING_HOTWORD_START: 'shs', - SHOWING_ERROR_MESSAGE: 'sem', - SHOWING_TIMEOUT_MESSAGE: 'stm', - CLICKED_RESUME: 'hcc', - CLICKED_RESTART: 'hcr', - CLICKED_DEBUG: 'hcd' -}; - -/** - * Errors that are sent to the hotword extension. - * @enum {string} - */ -AudioClient.Error = { - NO_SPEECH_UI: 'ac1', - NO_HOTWORD_STARTED_UI: 'ac2', - NO_HOTWORD_TIMEOUT_UI: 'ac3', - NO_HOTWORD_ERROR_UI: 'ac4' -}; - -/** - * @const {string} - * @private - */ -AudioClient.HOTWORD_EXTENSION_ID_ = 'nbpagnldghgfoolbancepceaanlmhfmd'; - -/** - * Number of times to retry checking a transient error. - * @const {number} - * @private - */ -AudioClient.MAX_RETRIES = 3; - -/** - * Delay to wait in milliseconds before rechecking for any transient errors. - * @const {number} - * @private - */ -AudioClient.RETRY_TIME_MS_ = 2000; - -/** - * DOM ID for the speech UI overlay. - * @const {string} - * @private - */ -AudioClient.SPEECH_UI_OVERLAY_ID_ = 'spch'; - -/** - * @const {string} - * @private - */ -AudioClient.HELP_CENTER_URL_ = - 'https://support.google.com/chrome/?p=ui_hotword_search'; - -/** - * @const {string} - * @private - */ -AudioClient.CLIENT_PORT_NAME_ = 'chwcpn'; - -/** - * Existence of the Audio Client. - * @const {string} - * @private - */ -AudioClient.EXISTS_ = 'chwace'; - -/** - * Checks for the presence of speech overlay UI DOM elements. - * @private - */ -AudioClient.prototype.checkSpeechOverlayUi_ = function() { - if (!this.speechOverlay_) { - window.setTimeout( - this.delayedCheckSpeechOverlayUi_.bind(this), - AudioClient.RETRY_TIME_MS_); - } else { - this.checkSpeechUiRetries_ = 0; - } -}; - -/** - * Function called to check for the speech UI overlay after some time has - * passed since an initial check. Will either retry triggering the speech - * or sends an error message depending on the number of retries. - * @private - */ -AudioClient.prototype.delayedCheckSpeechOverlayUi_ = function() { - this.speechOverlay_ = - document.getElementById(AudioClient.SPEECH_UI_OVERLAY_ID_); - if (!this.speechOverlay_) { - if (this.checkSpeechUiRetries_++ < AudioClient.MAX_RETRIES) { - this.sendCommandToPage_(AudioClient.CommandToPage.VOICE_TRIGGER); - this.checkSpeechOverlayUi_(); - } else { - this.sendCommandToExtension_(AudioClient.Error.NO_SPEECH_UI); - } - } else { - this.checkSpeechUiRetries_ = 0; - } -}; - -/** - * Checks that the triggered UI is actually displayed. - * @param {AudioClient.CommandToPage} command Command that was send. - * @private - */ -AudioClient.prototype.checkUi_ = function(command) { - this.uiStatus_[command].timeoutId = window.setTimeout( - this.failedCheckUi_.bind(this, command), AudioClient.RETRY_TIME_MS_); -}; - -/** - * Function called when the UI verification is not called in time. Will either - * retry the command or sends an error message, depending on the number of - * retries for the command. - * @param {AudioClient.CommandToPage} command Command that was sent. - * @private - */ -AudioClient.prototype.failedCheckUi_ = function(command) { - if (this.uiStatus_[command].tries++ < AudioClient.MAX_RETRIES) { - this.sendCommandToPage_(command); - this.checkUi_(command); - } else { - this.sendCommandToExtension_(this.uiStatus_[command].error); - } -}; - -/** - * Confirm that an UI element has been shown. - * @param {AudioClient.CommandToPage} command UI to confirm. - * @private - */ -AudioClient.prototype.verifyUi_ = function(command) { - if (this.uiStatus_[command].timeoutId) { - window.clearTimeout(this.uiStatus_[command].timeoutId); - this.uiStatus_[command].timeoutId = null; - this.uiStatus_[command].tries = 0; - } -}; - -/** - * Sends a command to the audio manager. - * @param {string} commandStr command to send to plugin. - * @private - */ -AudioClient.prototype.sendCommandToExtension_ = function(commandStr) { - if (this.port_) - this.port_.postMessage({'cmd': commandStr}); -}; - -/** - * Handles a message from the audio manager. - * @param {{cmd: string}} commandObj Command from the audio manager. - * @private - */ -AudioClient.prototype.handleCommandFromExtension_ = function(commandObj) { - var command = commandObj['cmd']; - if (command) { - switch (command) { - case AudioClient.CommandToPage.HOTWORD_VOICE_TRIGGER: - this.sendCommandToPage_(command); - this.checkSpeechOverlayUi_(); - break; - case AudioClient.CommandToPage.HOTWORD_STARTED: - this.sendCommandToPage_(command); - this.checkUi_(command); - break; - case AudioClient.CommandToPage.HOTWORD_ENDED: - this.sendCommandToPage_(command); - break; - case AudioClient.CommandToPage.HOTWORD_TIMEOUT: - this.sendCommandToPage_(command); - this.checkUi_(command); - break; - case AudioClient.CommandToPage.HOTWORD_ERROR: - this.sendCommandToPage_(command); - this.checkUi_(command); - break; - } - } -}; - -/** - * @param {AudioClient.CommandToPage} commandStr Command to send. - * @private - */ -AudioClient.prototype.sendCommandToPage_ = function(commandStr) { - window.postMessage({'type': commandStr}, '*'); -}; - -/** - * Handles a message from the html window. - * @param {!MessageEvent} messageEvent Message event from the window. - * @private - */ -AudioClient.prototype.handleCommandFromPage_ = function(messageEvent) { - if (messageEvent.source == window && messageEvent.data.type) { - var command = messageEvent.data.type; - switch (command) { - case AudioClient.CommandFromPage.SPEECH_START: - this.speechActive_ = true; - this.sendCommandToExtension_(command); - break; - case AudioClient.CommandFromPage.SPEECH_END: - this.speechActive_ = false; - this.sendCommandToExtension_(command); - break; - case AudioClient.CommandFromPage.SPEECH_RESET: - this.speechActive_ = false; - this.sendCommandToExtension_(command); - break; - case 'SPEECH_RESET': // Legacy, for embedded NTP. - this.speechActive_ = false; - this.sendCommandToExtension_(AudioClient.CommandFromPage.SPEECH_END); - break; - case AudioClient.CommandFromPage.CLICKED_RESUME: - this.sendCommandToExtension_(command); - break; - case AudioClient.CommandFromPage.CLICKED_RESTART: - this.sendCommandToExtension_(command); - break; - case AudioClient.CommandFromPage.CLICKED_DEBUG: - window.open(AudioClient.HELP_CENTER_URL_, '_blank'); - break; - case AudioClient.CommandFromPage.SHOWING_HOTWORD_START: - this.verifyUi_(AudioClient.CommandToPage.HOTWORD_STARTED); - break; - case AudioClient.CommandFromPage.SHOWING_ERROR_MESSAGE: - this.verifyUi_(AudioClient.CommandToPage.HOTWORD_ERROR); - break; - case AudioClient.CommandFromPage.SHOWING_TIMEOUT_MESSAGE: - this.verifyUi_(AudioClient.CommandToPage.HOTWORD_TIMEOUT); - break; - } - } -}; - -/** - * Initialize the content script. - */ -AudioClient.prototype.initialize = function() { - if (AudioClient.EXISTS_ in window) - return; - window[AudioClient.EXISTS_] = true; - - // UI verification object. - this.uiStatus_ = {}; - this.uiStatus_[AudioClient.CommandToPage.HOTWORD_STARTED] = { - timeoutId: null, - tries: 0, - error: AudioClient.Error.NO_HOTWORD_STARTED_UI - }; - this.uiStatus_[AudioClient.CommandToPage.HOTWORD_TIMEOUT] = { - timeoutId: null, - tries: 0, - error: AudioClient.Error.NO_HOTWORD_TIMEOUT_UI - }; - this.uiStatus_[AudioClient.CommandToPage.HOTWORD_ERROR] = { - timeoutId: null, - tries: 0, - error: AudioClient.Error.NO_HOTWORD_ERROR_UI - }; - - this.handleCommandFromPageFunc_ = this.handleCommandFromPage_.bind(this); - window.addEventListener('message', this.handleCommandFromPageFunc_, false); - this.initPort_(); -}; - -/** - * Initialize the communications port with the audio manager. This - * function will be also be called again if the audio-manager - * disconnects for some reason (such as the extension - * background.html page being reloaded). - * @private - */ -AudioClient.prototype.initPort_ = function() { - this.port_ = chrome.runtime.connect( - AudioClient.HOTWORD_EXTENSION_ID_, - {'name': AudioClient.CLIENT_PORT_NAME_}); - // Note that this listen may have to be destroyed manually if AudioClient - // is ever destroyed on this tab. - this.port_.onDisconnect.addListener( - (function(e) { - if (this.handleCommandFromPageFunc_) { - window.removeEventListener( - 'message', this.handleCommandFromPageFunc_, false); - } - delete window[AudioClient.EXISTS_]; - }).bind(this)); - - // See note above. - this.port_.onMessage.addListener(this.handleCommandFromExtension_.bind(this)); - - if (this.speechActive_) { - this.sendCommandToExtension_(AudioClient.CommandFromPage.SPEECH_START); - } else { - // It's possible for this script to be injected into the page after it has - // completed loaded (i.e. when prerendering). In this case, this script - // won't receive a SPEECH_RESET from the page to forward onto the - // extension. To make up for this, always send a SPEECH_RESET. This means - // in most cases, the extension will receive SPEECH_RESET twice, one from - // this sendCommandToExtension_ and the one forwarded from the page. But - // that's OK and the extension can handle it. - this.sendCommandToExtension_(AudioClient.CommandFromPage.SPEECH_RESET); - } -}; - -// Initializes as soon as the code is ready, do not wait for the page. -new AudioClient().initialize(); -})(); diff --git a/chromium/chrome/browser/resources/hotword/base_session_manager.js b/chromium/chrome/browser/resources/hotword/base_session_manager.js deleted file mode 100644 index 6891199d9a5..00000000000 --- a/chromium/chrome/browser/resources/hotword/base_session_manager.js +++ /dev/null @@ -1,152 +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('hotword', function() { - 'use strict'; - - /** - * Base class for managing hotwording sessions. - * @param {!hotword.StateManager} stateManager Manager of global hotwording - * state. - * @param {!hotword.constants.SessionSource} sessionSource Source of the - * hotword session request. - * @constructor - */ - function BaseSessionManager(stateManager, sessionSource) { - /** - * Manager of global hotwording state. - * @protected {!hotword.StateManager} - */ - this.stateManager = stateManager; - - /** - * Source of the hotword session request. - * @private {!hotword.constants.SessionSource} - */ - this.sessionSource_ = sessionSource; - - /** - * Chrome event listeners. Saved so that they can be de-registered when - * hotwording is disabled. - * @private - */ - this.sessionRequestedListener_ = this.handleSessionRequested_.bind(this); - this.sessionStoppedListener_ = this.handleSessionStopped_.bind(this); - - // Need to setup listeners on startup, otherwise events that caused the - // event page to start up, will be lost. - this.setupListeners_(); - - this.stateManager.onStatusChanged.addListener(function() { - hotword.debug('onStatusChanged'); - this.updateListeners(); - }.bind(this)); - } - - BaseSessionManager.prototype = { - /** - * Return whether or not this session type is enabled. - * @protected - * @return {boolean} - */ - enabled: assertNotReached, - - /** - * Called when the hotwording session is stopped. - * @protected - */ - onSessionStop: function() {}, - - /** - * Starts a launcher hotwording session. - * @param {hotword.constants.TrainingMode=} opt_mode The mode to start the - * recognizer in. - */ - startSession: function(opt_mode) { - this.stateManager.startSession(this.sessionSource_, function() { - chrome.hotwordPrivate.setHotwordSessionState(true, function() {}); - }, this.handleHotwordTrigger.bind(this), opt_mode); - }, - - /** - * Stops a launcher hotwording session. - * @private - */ - stopSession_: function() { - this.stateManager.stopSession(this.sessionSource_); - this.onSessionStop(); - }, - - /** - * Handles a hotword triggered event. - * @param {?Object} log Audio log data, if audio logging is enabled. - * @protected - */ - handleHotwordTrigger: function(log) { - hotword.debug('Hotword triggered: ' + this.sessionSource_, log); - chrome.hotwordPrivate.notifyHotwordRecognition( - 'search', log, function() {}); - }, - - /** - * Handles a hotwordPrivate.onHotwordSessionRequested event. - * @private - */ - handleSessionRequested_: function() { - hotword.debug('handleSessionRequested_: ' + this.sessionSource_); - this.startSession(); - }, - - /** - * Handles a hotwordPrivate.onHotwordSessionStopped event. - * @private - */ - handleSessionStopped_: function() { - hotword.debug('handleSessionStopped_: ' + this.sessionSource_); - this.stopSession_(); - }, - - /** - * Set up event listeners. - * @private - */ - setupListeners_: function() { - if (chrome.hotwordPrivate.onHotwordSessionRequested.hasListener( - this.sessionRequestedListener_)) { - return; - } - - chrome.hotwordPrivate.onHotwordSessionRequested.addListener( - this.sessionRequestedListener_); - chrome.hotwordPrivate.onHotwordSessionStopped.addListener( - this.sessionStoppedListener_); - }, - - /** - * Remove event listeners. - * @private - */ - removeListeners_: function() { - chrome.hotwordPrivate.onHotwordSessionRequested.removeListener( - this.sessionRequestedListener_); - chrome.hotwordPrivate.onHotwordSessionStopped.removeListener( - this.sessionStoppedListener_); - }, - - /** - * Update event listeners based on the current hotwording state. - * @protected - */ - updateListeners: function() { - if (this.enabled()) { - this.setupListeners_(); - } else { - this.removeListeners_(); - this.stopSession_(); - } - } - }; - - return {BaseSessionManager: BaseSessionManager}; -}); diff --git a/chromium/chrome/browser/resources/hotword/constants.js b/chromium/chrome/browser/resources/hotword/constants.js deleted file mode 100644 index 0fbfa0e8a45..00000000000 --- a/chromium/chrome/browser/resources/hotword/constants.js +++ /dev/null @@ -1,299 +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('hotword.constants', function() { - 'use strict'; - - /** - * Number of seconds of audio to record when logging is enabled. - * @const {number} - */ - var AUDIO_LOG_SECONDS = 2; - - /** - * Timeout in seconds, for detecting false positives with a hotword stream. - * @const {number} - */ - var HOTWORD_STREAM_TIMEOUT_SECONDS = 2; - - /** - * Hotword data shared module extension's ID. - * @const {string} - */ - var SHARED_MODULE_ID = 'lccekmodgklaepjeofjdjpbminllajkg'; - - /** - * Path to shared module data. - * @const {string} - */ - var SHARED_MODULE_ROOT = '_modules/' + SHARED_MODULE_ID; - - /** - * Name used by the content scripts to create communications Ports. - * @const {string} - */ - var CLIENT_PORT_NAME = 'chwcpn'; - - /** - * The field name to specify the command among pages. - * @const {string} - */ - var COMMAND_FIELD_NAME = 'cmd'; - - /** - * The speaker model file name. - * @const {string} - */ - var SPEAKER_MODEL_FILE_NAME = 'speaker_model.data'; - - /** - * The training utterance file name prefix. - * @const {string} - */ - var UTTERANCE_FILE_PREFIX = 'utterance-'; - - /** - * The training utterance file extension. - * @const {string} - */ - var UTTERANCE_FILE_EXTENSION = '.raw'; - - /** - * The number of training utterances required to train the speaker model. - * @const {number} - */ - var NUM_TRAINING_UTTERANCES = 3; - - /** - * The size of the file system requested for reading the speaker model and - * utterances. This number should always be larger than the combined file - * size, - * currently 576338 bytes as of February 2015. - * @const {number} - */ - var FILE_SYSTEM_SIZE_BYTES = 1048576; - - /** - * Time to wait for expected messages, in milliseconds. - * @enum {number} - */ - var TimeoutMs = {SHORT: 200, NORMAL: 500, LONG: 2000}; - - /** - * The URL of the files used by the plugin. - * @enum {string} - */ - var File = { - RECOGNIZER_CONFIG: 'hotword.data', - }; - - /** - * Errors emitted by the NaClManager. - * @enum {string} - */ - var Error = { - NACL_CRASH: 'nacl_crash', - TIMEOUT: 'timeout', - }; - - /** - * Event types supported by NaClManager. - * @enum {string} - */ - var Event = { - READY: 'ready', - TRIGGER: 'trigger', - SPEAKER_MODEL_SAVED: 'speaker model saved', - ERROR: 'error', - TIMEOUT: 'timeout', - }; - - /** - * Messages for communicating with the NaCl recognizer plugin. These must - * match - * constants in <google3>/hotword_plugin.c - * @enum {string} - */ - var NaClPlugin = { - RESTART: 'r', - SAMPLE_RATE_PREFIX: 'h', - MODEL_PREFIX: 'm', - STOP: 's', - LOG: 'l', - DSP: 'd', - BEGIN_SPEAKER_MODEL: 'b', - ADAPT_SPEAKER_MODEL: 'a', - FINISH_SPEAKER_MODEL: 'f', - SPEAKER_MODEL_SAVED: 'sm_saved', - REQUEST_MODEL: 'model', - MODEL_LOADED: 'model_loaded', - READY_FOR_AUDIO: 'audio', - STOPPED: 'stopped', - HOTWORD_DETECTED: 'hotword', - MS_CONFIGURED: 'ms_configured', - TIMEOUT: 'timeout' - }; - - /** - * Messages sent from the injected scripts to the Google page. - * @enum {string} - */ - var CommandToPage = { - HOTWORD_VOICE_TRIGGER: 'vt', - HOTWORD_STARTED: 'hs', - HOTWORD_ENDED: 'hd', - HOTWORD_TIMEOUT: 'ht', - HOTWORD_ERROR: 'he' - }; - - /** - * Messages sent from the Google page to the extension or to the - * injected script and then passed to the extension. - * @enum {string} - */ - var CommandFromPage = { - SPEECH_START: 'ss', - SPEECH_END: 'se', - SPEECH_RESET: 'sr', - SHOWING_HOTWORD_START: 'shs', - SHOWING_ERROR_MESSAGE: 'sem', - SHOWING_TIMEOUT_MESSAGE: 'stm', - CLICKED_RESUME: 'hcc', - CLICKED_RESTART: 'hcr', - CLICKED_DEBUG: 'hcd', - WAKE_UP_HELPER: 'wuh', - // Command specifically for the opt-in promo below this line. - // User has explicitly clicked 'no'. - CLICKED_NO_OPTIN: 'hcno', - // User has opted in. - CLICKED_OPTIN: 'hco', - // User clicked on the microphone. - PAGE_WAKEUP: 'wu' - }; - - /** - * Source of a hotwording session request. - * @enum {string} - */ - var SessionSource = { - LAUNCHER: 'launcher', - NTP: 'ntp', - ALWAYS: 'always', - TRAINING: 'training' - }; - - /** - * The mode to start the hotword recognizer in. - * @enum {string} - */ - var RecognizerStartMode = { - NORMAL: 'normal', - NEW_MODEL: 'new model', - ADAPT_MODEL: 'adapt model' - }; - - /** - * MediaStream open success/errors to be reported via UMA. - * DO NOT remove or renumber values in this enum. Only add new ones. - * @enum {number} - */ - var UmaMediaStreamOpenResult = { - SUCCESS: 0, - UNKNOWN: 1, - NOT_SUPPORTED: 2, - PERMISSION_DENIED: 3, - CONSTRAINT_NOT_SATISFIED: 4, - OVERCONSTRAINED: 5, - NOT_FOUND: 6, - ABORT: 7, - SOURCE_UNAVAILABLE: 8, - PERMISSION_DISMISSED: 9, - INVALID_STATE: 10, - DEVICES_NOT_FOUND: 11, - INVALID_SECURITY_ORIGIN: 12, - MAX: 12 - }; - - /** - * UMA metrics. - * DO NOT change these enum values. - * @enum {string} - */ - var UmaMetrics = { - TRIGGER: 'Hotword.HotwordTrigger', - MEDIA_STREAM_RESULT: 'Hotword.HotwordMediaStreamResult', - NACL_PLUGIN_LOAD_RESULT: 'Hotword.HotwordNaClPluginLoadResult', - NACL_MESSAGE_TIMEOUT: 'Hotword.HotwordNaClMessageTimeout', - TRIGGER_SOURCE: 'Hotword.HotwordTriggerSource' - }; - - /** - * Message waited for by NaCl plugin, to be reported via UMA. - * DO NOT remove or renumber values in this enum. Only add new ones. - * @enum {number} - */ - var UmaNaClMessageTimeout = { - REQUEST_MODEL: 0, - MODEL_LOADED: 1, - READY_FOR_AUDIO: 2, - STOPPED: 3, - HOTWORD_DETECTED: 4, - MS_CONFIGURED: 5, - MAX: 5 - }; - - /** - * NaCl plugin load success/errors to be reported via UMA. - * DO NOT remove or renumber values in this enum. Only add new ones. - * @enum {number} - */ - var UmaNaClPluginLoadResult = - {SUCCESS: 0, UNKNOWN: 1, CRASH: 2, NO_MODULE_FOUND: 3, MAX: 3}; - - /** - * Source of hotword triggering, to be reported via UMA. - * DO NOT remove or renumber values in this enum. Only add new ones. - * @enum {number} - */ - var UmaTriggerSource = - {LAUNCHER: 0, NTP_GOOGLE_COM: 1, ALWAYS_ON: 2, TRAINING: 3, MAX: 3}; - - /** - * The browser UI language. - * @const {string} - */ - var UI_LANGUAGE = (chrome.i18n && chrome.i18n.getUILanguage) ? - chrome.i18n.getUILanguage() : - ''; - - return { - AUDIO_LOG_SECONDS: AUDIO_LOG_SECONDS, - CLIENT_PORT_NAME: CLIENT_PORT_NAME, - COMMAND_FIELD_NAME: COMMAND_FIELD_NAME, - FILE_SYSTEM_SIZE_BYTES: FILE_SYSTEM_SIZE_BYTES, - HOTWORD_STREAM_TIMEOUT_SECONDS: HOTWORD_STREAM_TIMEOUT_SECONDS, - NUM_TRAINING_UTTERANCES: NUM_TRAINING_UTTERANCES, - SHARED_MODULE_ID: SHARED_MODULE_ID, - SHARED_MODULE_ROOT: SHARED_MODULE_ROOT, - SPEAKER_MODEL_FILE_NAME: SPEAKER_MODEL_FILE_NAME, - UI_LANGUAGE: UI_LANGUAGE, - UTTERANCE_FILE_EXTENSION: UTTERANCE_FILE_EXTENSION, - UTTERANCE_FILE_PREFIX: UTTERANCE_FILE_PREFIX, - CommandToPage: CommandToPage, - CommandFromPage: CommandFromPage, - Error: Error, - Event: Event, - File: File, - NaClPlugin: NaClPlugin, - RecognizerStartMode: RecognizerStartMode, - SessionSource: SessionSource, - TimeoutMs: TimeoutMs, - UmaMediaStreamOpenResult: UmaMediaStreamOpenResult, - UmaMetrics: UmaMetrics, - UmaNaClMessageTimeout: UmaNaClMessageTimeout, - UmaNaClPluginLoadResult: UmaNaClPluginLoadResult, - UmaTriggerSource: UmaTriggerSource - }; - -}); diff --git a/chromium/chrome/browser/resources/hotword/keep_alive.js b/chromium/chrome/browser/resources/hotword/keep_alive.js deleted file mode 100644 index 20ff2eabb9d..00000000000 --- a/chromium/chrome/browser/resources/hotword/keep_alive.js +++ /dev/null @@ -1,52 +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('hotword', function() { - 'use strict'; - - /** - * Class used to keep this extension alive. When started, this calls an - * extension API on a regular basis which resets the event page keep-alive - * timer. - * @constructor - */ - function KeepAlive() { - this.timeoutId_ = null; - } - - KeepAlive.prototype = { - /** - * Start the keep alive process. Safe to call multiple times. - */ - start: function() { - if (this.timeoutId_ == null) - this.timeoutId_ = setTimeout(this.handleTimeout_.bind(this), 1000); - }, - - /** - * Stops the keep alive process. Safe to call multiple times. - */ - stop: function() { - if (this.timeoutId_ != null) { - clearTimeout(this.timeoutId_); - this.timeoutId_ = null; - } - }, - - /** - * Handle the timer timeout. Calls an extension API and schedules the next - * timeout. - * @private - */ - handleTimeout_: function() { - // Dummy extensions API call used to keep this event page alive by - // resetting the shutdown timer. - chrome.runtime.getPlatformInfo(function(info) {}); - - this.timeoutId_ = setTimeout(this.handleTimeout_.bind(this), 1000); - } - }; - - return {KeepAlive: KeepAlive}; -}); diff --git a/chromium/chrome/browser/resources/hotword/launcher_manager.js b/chromium/chrome/browser/resources/hotword/launcher_manager.js deleted file mode 100644 index 144c247eec4..00000000000 --- a/chromium/chrome/browser/resources/hotword/launcher_manager.js +++ /dev/null @@ -1,35 +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('hotword', function() { - 'use strict'; - - /** - * Class used to manage the interaction between hotwording and the launcher - * (app list). - * @param {!hotword.StateManager} stateManager - * @constructor - * @extends {hotword.BaseSessionManager} - */ - function LauncherManager(stateManager) { - hotword.BaseSessionManager.call( - this, stateManager, hotword.constants.SessionSource.LAUNCHER); - } - - LauncherManager.prototype = { - __proto__: hotword.BaseSessionManager.prototype, - - /** @override */ - enabled: function() { - return this.stateManager.isSometimesOnEnabled(); - }, - - /** @override */ - onSessionStop: function() { - chrome.hotwordPrivate.setHotwordSessionState(false, function() {}); - } - }; - - return {LauncherManager: LauncherManager}; -}); diff --git a/chromium/chrome/browser/resources/hotword/logging.js b/chromium/chrome/browser/resources/hotword/logging.js deleted file mode 100644 index e0744e07a42..00000000000 --- a/chromium/chrome/browser/resources/hotword/logging.js +++ /dev/null @@ -1,19 +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('hotword', function() { - 'use strict'; - - /** - * Wrapper around console.log allowing debug log message to be enabled during - * development. - * @param {...*} varArgs - */ - function debug(varArgs) { - if (hotword.DEBUG || window.localStorage['hotword.DEBUG']) - console.log.apply(console, arguments); - } - - return {DEBUG: false, debug: debug}; -}); diff --git a/chromium/chrome/browser/resources/hotword/manager.js b/chromium/chrome/browser/resources/hotword/manager.js deleted file mode 100644 index f4ac1473c9a..00000000000 --- a/chromium/chrome/browser/resources/hotword/manager.js +++ /dev/null @@ -1,50 +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. - -(function() { -'use strict'; - -/** - * @fileoverview This extension provides hotword triggering capabilites to - * Chrome. - * - * This extension contains all the JavaScript for loading and managing the - * hotword detector. The hotword detector and language model data will be - * provided by a shared module loaded from the web store. - * - * IMPORTANT! Whenever adding new events, the extension version number MUST be - * incremented. - */ - -// Hotwording state. -var stateManager = new hotword.StateManager(); -var pageAudioManager = new hotword.PageAudioManager(stateManager); -var alwaysOnManager = new hotword.AlwaysOnManager(stateManager); -var launcherManager = new hotword.LauncherManager(stateManager); -var trainingManager = new hotword.TrainingManager(stateManager); - -// Detect when hotword settings have changed. -chrome.hotwordPrivate.onEnabledChanged.addListener(function() { - stateManager.updateStatus(); -}); - -// Detect a request to delete the speaker model. -chrome.hotwordPrivate.onDeleteSpeakerModel.addListener(function() { - hotword.TrainingManager.handleDeleteSpeakerModel(); -}); - -// Detect a request for the speaker model existence. -chrome.hotwordPrivate.onSpeakerModelExists.addListener(function() { - hotword.TrainingManager.handleSpeakerModelExists(); -}); - -// Detect when the shared module containing the NaCL module and language model -// is installed. -chrome.management.onInstalled.addListener(function(info) { - if (info.id == hotword.constants.SHARED_MODULE_ID) { - hotword.debug('Shared module installed, reloading extension.'); - chrome.runtime.reload(); - } -}); -}()); diff --git a/chromium/chrome/browser/resources/hotword/manifest.json b/chromium/chrome/browser/resources/hotword/manifest.json deleted file mode 100644 index e22ff936ddb..00000000000 --- a/chromium/chrome/browser/resources/hotword/manifest.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - // Extension ID: nbpagnldghgfoolbancepceaanlmhfmd - "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbHXRPiq2De9EJ+4pvNN6uE/D2avxrqyLSpA/Hq3II+btkPl1gboY3oUPTfevpVOFa90Y1c1b3/W682dXqybT0klIvFLKhdQx0LiVqSUQyIaDrwOCSo/ZcukbEwDRojegWymCjHvX6WZk4kKZzTJYzY1vrp0TWKLhttEMN9KFmowIDAQAB", - - "name": "Hotword triggering", - "version": "0.0.1.4", - "manifest_version": 2, - - "background": { - "scripts": [ - "chrome://resources/js/cr.js", - "chrome://resources/js/util.js", - "chrome://resources/js/cr/event_target.js", - "constants.js", - "keep_alive.js", - "logging.js", - "metrics.js", - "nacl_manager.js", - "state_manager.js", - "base_session_manager.js", - "always_on_manager.js", - "launcher_manager.js", - "page_audio_manager.js", - "training_manager.js", - "manager.js" - ], - "persistent": false - }, - - "permissions": [ - "*://*.google.at/*", - "*://*.google.ca/*", - "*://*.google.com/*", - "*://*.google.com.au/*", - "*://*.google.com.mx/*", - "*://*.google.com.br/*", - "*://*.google.co.jp/*", - "*://*.google.co.kr/*", - "*://*.google.co.nz/*", - "*://*.google.co.uk/*", - "*://*.google.co.za/*", - "*://*.google.de/*", - "*://*.google.es/*", - "*://*.google.fr/*", - "*://*.google.it/*", - "*://*.google.ru/*", - "chrome://newtab/", - "chrome://resources/", - "audioCapture", - "hotwordPrivate", - "idle", - "management", - "metricsPrivate", - "tabs", - "unlimitedStorage" - ], - - "externally_connectable": { - "matches": [ - "*://*.google.at/*", - "*://*.google.ca/*", - "*://*.google.com/*", - "*://*.google.com.au/*", - "*://*.google.com.mx/*", - "*://*.google.com.br/*", - "*://*.google.co.jp/*", - "*://*.google.co.kr/*", - "*://*.google.co.nz/*", - "*://*.google.co.uk/*", - "*://*.google.co.za/*", - "*://*.google.de/*", - "*://*.google.es/*", - "*://*.google.fr/*", - "*://*.google.it/*", - "*://*.google.ru/*", - "chrome://newtab/" - ] - }, - - "import": [ - { - "id": "lccekmodgklaepjeofjdjpbminllajkg" - } - ], - - "content_security_policy": "object-src 'none'; script-src chrome://resources 'self' blob: filesystem:", - "minimum_chrome_version": "38" -} diff --git a/chromium/chrome/browser/resources/hotword/metrics.js b/chromium/chrome/browser/resources/hotword/metrics.js deleted file mode 100644 index 7d4001bf3ec..00000000000 --- a/chromium/chrome/browser/resources/hotword/metrics.js +++ /dev/null @@ -1,26 +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('hotword.metrics', function() { - 'use strict'; - - /** - * Helper function to record enum values in UMA. - * @param {!string} name - * @param {!number} value - * @param {!number} maxValue - */ - function recordEnum(name, value, maxValue) { - var metricDesc = { - 'metricName': name, - 'type': chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LINEAR, - 'min': 1, - 'max': maxValue, - 'buckets': maxValue + 1 - }; - chrome.metricsPrivate.recordValue(metricDesc, value); - } - - return {recordEnum: recordEnum}; -}); diff --git a/chromium/chrome/browser/resources/hotword/nacl_manager.js b/chromium/chrome/browser/resources/hotword/nacl_manager.js deleted file mode 100644 index 015399f346c..00000000000 --- a/chromium/chrome/browser/resources/hotword/nacl_manager.js +++ /dev/null @@ -1,612 +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('hotword', function() { - 'use strict'; - - /** - * Class used to manage the state of the NaCl recognizer plugin. Handles all - * control of the NaCl plugin, including creation, start, stop, trigger, and - * shutdown. - * - * @param {boolean} loggingEnabled Whether audio logging is enabled. - * @param {boolean} hotwordStream Whether the audio input stream is from a - * hotword stream. - * @constructor - * @extends {cr.EventTarget} - */ - function NaClManager(loggingEnabled, hotwordStream) { - /** - * Current state of this manager. - * @private {hotword.NaClManager.ManagerState_} - */ - this.recognizerState_ = ManagerState_.UNINITIALIZED; - - /** - * The window.timeout ID associated with a pending message. - * @private {?number} - */ - this.naclTimeoutId_ = null; - - /** - * The expected message that will cancel the current timeout. - * @private {?string} - */ - this.expectingMessage_ = null; - - /** - * Whether the plugin will be started as soon as it stops. - * @private {boolean} - */ - this.restartOnStop_ = false; - - /** - * NaCl plugin element on extension background page. - * @private {?HTMLEmbedElement} - */ - this.plugin_ = null; - - /** - * URL containing hotword-model data file. - * @private {string} - */ - this.modelUrl_ = ''; - - /** - * Media stream containing an audio input track. - * @private {?MediaStream} - */ - this.stream_ = null; - - /** - * The mode to start the recognizer in. - * @private {?chrome.hotwordPrivate.RecognizerStartMode} - */ - this.startMode_ = hotword.constants.RecognizerStartMode.NORMAL; - - /** - * Whether audio logging is enabled. - * @private {boolean} - */ - this.loggingEnabled_ = loggingEnabled; - - /** - * Whether the audio input stream is from a hotword stream. - * @private {boolean} - */ - this.hotwordStream_ = hotwordStream; - - /** - * Audio log of X seconds before hotword triggered. - * @private {?Object} - */ - this.preambleLog_ = null; - } - - /** - * States this manager can be in. Since messages to/from the plugin are - * asynchronous (and potentially queued), it's not possible to know what state - * the plugin is in. However, track a state machine for NaClManager based on - * what messages are sent/received. - * @enum {number} - * @private - */ - NaClManager.ManagerState_ = { - UNINITIALIZED: 0, - LOADING: 1, - STOPPING: 2, - STOPPED: 3, - STARTING: 4, - RUNNING: 5, - ERROR: 6, - SHUTDOWN: 7, - }; - var ManagerState_ = NaClManager.ManagerState_; - var Error_ = hotword.constants.Error; - var UmaNaClMessageTimeout_ = hotword.constants.UmaNaClMessageTimeout; - var UmaNaClPluginLoadResult_ = hotword.constants.UmaNaClPluginLoadResult; - - NaClManager.prototype.__proto__ = cr.EventTarget.prototype; - - /** - * Called when an error occurs. Dispatches an event. - * @param {!hotword.constants.Error} error - * @private - */ - NaClManager.prototype.handleError_ = function(error) { - var event = new Event(hotword.constants.Event.ERROR); - event.data = error; - this.dispatchEvent(event); - }; - - /** - * Record the result of loading the NaCl plugin to UMA. - * @param {!hotword.constants.UmaNaClPluginLoadResult} error - * @private - */ - NaClManager.prototype.logPluginLoadResult_ = function(error) { - hotword.metrics.recordEnum( - hotword.constants.UmaMetrics.NACL_PLUGIN_LOAD_RESULT, error, - UmaNaClPluginLoadResult_.MAX); - }; - - /** - * Set a timeout. Only allow one timeout to exist at any given time. - * @param {!function()} func - * @param {number} timeout - * @private - */ - NaClManager.prototype.setTimeout_ = function(func, timeout) { - assert(!this.naclTimeoutId_, 'Timeout already exists'); - this.naclTimeoutId_ = window.setTimeout(function() { - this.naclTimeoutId_ = null; - func(); - }.bind(this), timeout); - }; - - /** - * Clears the current timeout. - * @private - */ - NaClManager.prototype.clearTimeout_ = function() { - window.clearTimeout(this.naclTimeoutId_); - this.naclTimeoutId_ = null; - }; - - /** - * Starts a stopped or stopping hotword recognizer (NaCl plugin). - * @param {hotword.constants.RecognizerStartMode} mode The mode to start the - * recognizer in. - */ - NaClManager.prototype.startRecognizer = function(mode) { - this.startMode_ = mode; - if (this.recognizerState_ == ManagerState_.STOPPED) { - this.preambleLog_ = null; - this.recognizerState_ = ManagerState_.STARTING; - if (mode == hotword.constants.RecognizerStartMode.NEW_MODEL) { - hotword.debug('Starting Recognizer in START training mode'); - this.sendDataToPlugin_( - hotword.constants.NaClPlugin.BEGIN_SPEAKER_MODEL); - } else if (mode == hotword.constants.RecognizerStartMode.ADAPT_MODEL) { - hotword.debug('Starting Recognizer in ADAPT training mode'); - this.sendDataToPlugin_( - hotword.constants.NaClPlugin.ADAPT_SPEAKER_MODEL); - } else { - hotword.debug('Starting Recognizer in NORMAL mode'); - this.sendDataToPlugin_(hotword.constants.NaClPlugin.RESTART); - } - // Normally, there would be a waitForMessage_(READY_FOR_AUDIO) here. - // However, this message is sent the first time audio data is read and in - // some cases (ie. using the hotword stream), this won't happen until a - // potential hotword trigger is seen. Having a waitForMessage_() would - // time - // out in this case, so just leave it out. This ends up sacrificing a bit - // of - // error detection in the non-hotword-stream case, but I think we can live - // with that. - } else if (this.recognizerState_ == ManagerState_.STOPPING) { - // Wait until the plugin is stopped before trying to start it. - this.restartOnStop_ = true; - } else { - throw 'Attempting to start NaCl recogniser not in STOPPED or STOPPING ' + - 'state'; - } - }; - - /** - * Stops the hotword recognizer. - */ - NaClManager.prototype.stopRecognizer = function() { - if (this.recognizerState_ == ManagerState_.STARTING) { - // If the recognizer is stopped before it finishes starting, it causes an - // assertion to be raised in waitForMessage_() since we're waiting for the - // READY_FOR_AUDIO message. Clear the current timeout and expecting - // message - // since we no longer expect it and may never receive it. - this.clearTimeout_(); - this.expectingMessage_ = null; - } - this.sendDataToPlugin_(hotword.constants.NaClPlugin.STOP); - this.recognizerState_ = ManagerState_.STOPPING; - this.waitForMessage_( - hotword.constants.TimeoutMs.NORMAL, - hotword.constants.NaClPlugin.STOPPED); - }; - - /** - * Saves the speaker model. - */ - NaClManager.prototype.finalizeSpeakerModel = function() { - if (this.recognizerState_ == ManagerState_.UNINITIALIZED || - this.recognizerState_ == ManagerState_.ERROR || - this.recognizerState_ == ManagerState_.SHUTDOWN || - this.recognizerState_ == ManagerState_.LOADING) { - return; - } - this.sendDataToPlugin_(hotword.constants.NaClPlugin.FINISH_SPEAKER_MODEL); - }; - - /** - * Checks whether the file at the given path exists. - * @param {!string} path Path to a file. Can be any valid URL. - * @return {boolean} True if the patch exists. - * @private - */ - NaClManager.prototype.fileExists_ = function(path) { - var xhr = new XMLHttpRequest(); - xhr.open('HEAD', path, false); - try { - xhr.send(); - } catch (err) { - return false; - } - if (xhr.readyState != xhr.DONE || xhr.status != 200) { - return false; - } - return true; - }; - - /** - * Creates and returns a list of possible languages to check for hotword - * support. - * @return {!Array<string>} Array of languages. - * @private - */ - NaClManager.prototype.getPossibleLanguages_ = function() { - // Create array used to search first for language-country, if not found then - // search for language, if not found then no language (empty string). - // For example, search for 'en-us', then 'en', then ''. - var langs = new Array(); - if (hotword.constants.UI_LANGUAGE) { - // Chrome webstore doesn't support uppercase path: crbug.com/353407 - var language = hotword.constants.UI_LANGUAGE.toLowerCase(); - langs.push(language); // Example: 'en-us'. - // Remove country to add just the language to array. - var hyphen = language.lastIndexOf('-'); - if (hyphen >= 0) { - langs.push(language.substr(0, hyphen)); // Example: 'en'. - } - } - langs.push(''); - return langs; - }; - - /** - * Creates a NaCl plugin object and attaches it to the page. - * @param {!string} src Location of the plugin. - * @return {!HTMLEmbedElement} NaCl plugin DOM object. - * @private - */ - NaClManager.prototype.createPlugin_ = function(src) { - var plugin = - /** @type {HTMLEmbedElement} */ (document.createElement('embed')); - plugin.src = src; - plugin.type = 'application/x-nacl'; - document.body.appendChild(plugin); - return plugin; - }; - - /** - * Initializes the NaCl manager. - * @param {!string} naclArch Either 'arm', 'x86-32' or 'x86-64'. - * @param {!MediaStream} stream A stream containing an audio source track. - * @return {boolean} True if the successful. - */ - NaClManager.prototype.initialize = function(naclArch, stream) { - assert( - this.recognizerState_ == ManagerState_.UNINITIALIZED, - 'Recognizer not in uninitialized state. State: ' + - this.recognizerState_); - assert(this.plugin_ == null); - var langs = this.getPossibleLanguages_(); - var i, j; - // For country-lang variations. For example, when combined with path it will - // attempt to find: '/x86-32_en-gb/', else '/x86-32_en/', else '/x86-32_/'. - for (i = 0; i < langs.length; i++) { - var folder = hotword.constants.SHARED_MODULE_ROOT + - '/_platform_specific/' + naclArch + '_' + langs[i] + '/'; - var dataSrc = folder + hotword.constants.File.RECOGNIZER_CONFIG; - var pluginSrc = hotword.constants.SHARED_MODULE_ROOT + '/hotword_' + - langs[i] + '.nmf'; - var dataExists = this.fileExists_(dataSrc) && this.fileExists_(pluginSrc); - if (!dataExists) { - continue; - } - - var plugin = this.createPlugin_(pluginSrc); - if (!plugin || !plugin.postMessage) { - document.body.removeChild(plugin); - this.recognizerState_ = ManagerState_.ERROR; - return false; - } - this.plugin_ = plugin; - this.modelUrl_ = chrome.extension.getURL(dataSrc); - this.stream_ = stream; - this.recognizerState_ = ManagerState_.LOADING; - - plugin.addEventListener( - 'message', this.handlePluginMessage_.bind(this), false); - - plugin.addEventListener('crash', function() { - this.handleError_(Error_.NACL_CRASH); - this.logPluginLoadResult_(UmaNaClPluginLoadResult_.CRASH); - }.bind(this), false); - return true; - } - this.recognizerState_ = ManagerState_.ERROR; - this.logPluginLoadResult_(UmaNaClPluginLoadResult_.NO_MODULE_FOUND); - return false; - }; - - /** - * Shuts down the NaCl plugin and frees all resources. - */ - NaClManager.prototype.shutdown = function() { - if (this.plugin_ != null) { - document.body.removeChild(this.plugin_); - this.plugin_ = null; - } - this.clearTimeout_(); - this.recognizerState_ = ManagerState_.SHUTDOWN; - if (this.stream_) - this.stream_.getAudioTracks()[0].stop(); - this.stream_ = null; - }; - - /** - * Sends data to the NaCl plugin. - * @param {!string|!MediaStreamTrack} data Command to be sent to NaCl plugin. - * @private - */ - NaClManager.prototype.sendDataToPlugin_ = function(data) { - assert( - this.recognizerState_ != ManagerState_.UNINITIALIZED, - 'Recognizer in uninitialized state'); - this.plugin_.postMessage(data); - }; - - /** - * Waits, with a timeout, for a message to be received from the plugin. If the - * message is not seen within the timeout, dispatch an 'error' event and go - * into - * the ERROR state. - * @param {number} timeout Timeout, in milliseconds, to wait for the message. - * @param {!string} message Message to wait for. - * @private - */ - NaClManager.prototype.waitForMessage_ = function(timeout, message) { - assert( - this.expectingMessage_ == null, - 'Cannot wait for message: ' + message + - ', already waiting for message ' + this.expectingMessage_); - this.setTimeout_(function() { - this.recognizerState_ = ManagerState_.ERROR; - this.handleError_(Error_.TIMEOUT); - switch (this.expectingMessage_) { - case hotword.constants.NaClPlugin.REQUEST_MODEL: - var metricValue = UmaNaClMessageTimeout_.REQUEST_MODEL; - break; - case hotword.constants.NaClPlugin.MODEL_LOADED: - var metricValue = UmaNaClMessageTimeout_.MODEL_LOADED; - break; - case hotword.constants.NaClPlugin.READY_FOR_AUDIO: - var metricValue = UmaNaClMessageTimeout_.READY_FOR_AUDIO; - break; - case hotword.constants.NaClPlugin.STOPPED: - var metricValue = UmaNaClMessageTimeout_.STOPPED; - break; - case hotword.constants.NaClPlugin.HOTWORD_DETECTED: - var metricValue = UmaNaClMessageTimeout_.HOTWORD_DETECTED; - break; - case hotword.constants.NaClPlugin.MS_CONFIGURED: - var metricValue = UmaNaClMessageTimeout_.MS_CONFIGURED; - break; - } - hotword.metrics.recordEnum( - hotword.constants.UmaMetrics.NACL_MESSAGE_TIMEOUT, metricValue, - UmaNaClMessageTimeout_.MAX); - }.bind(this), timeout); - this.expectingMessage_ = message; - }; - - /** - * Called when a message is received from the plugin. If we're waiting for - * that - * message, cancel the pending timeout. - * @param {string} message Message received. - * @private - */ - NaClManager.prototype.receivedMessage_ = function(message) { - if (message == this.expectingMessage_) { - this.clearTimeout_(); - this.expectingMessage_ = null; - } - }; - - /** - * Handle a REQUEST_MODEL message from the plugin. - * The plugin sends this message immediately after starting. - * @private - */ - NaClManager.prototype.handleRequestModel_ = function() { - if (this.recognizerState_ != ManagerState_.LOADING) { - return; - } - this.logPluginLoadResult_(UmaNaClPluginLoadResult_.SUCCESS); - this.sendDataToPlugin_( - hotword.constants.NaClPlugin.MODEL_PREFIX + this.modelUrl_); - this.waitForMessage_( - hotword.constants.TimeoutMs.LONG, - hotword.constants.NaClPlugin.MODEL_LOADED); - - // Configure logging in the plugin. This can be configured any time before - // starting the recognizer, and now is as good a time as any. - if (this.loggingEnabled_) { - this.sendDataToPlugin_( - hotword.constants.NaClPlugin.LOG + ':' + - hotword.constants.AUDIO_LOG_SECONDS); - } - - // If the audio stream is from a hotword stream, tell the plugin. - if (this.hotwordStream_) { - this.sendDataToPlugin_( - hotword.constants.NaClPlugin.DSP + ':' + - hotword.constants.HOTWORD_STREAM_TIMEOUT_SECONDS); - } - }; - - /** - * Handle a MODEL_LOADED message from the plugin. - * The plugin sends this message after successfully loading the language - * model. - * @private - */ - NaClManager.prototype.handleModelLoaded_ = function() { - if (this.recognizerState_ != ManagerState_.LOADING) { - return; - } - this.sendDataToPlugin_(this.stream_.getAudioTracks()[0]); - // The plugin will send a MS_CONFIGURED, but don't set a timeout waiting for - // it. MediaStreamAudioTrack::Configure() will remain pending until the - // first - // audio buffer is received. When the audio source is a DSP for always-on - // detection, no audio sample is sent until the DSP detects a potential - // hotword trigger. Thus, Configure would remain pending indefinitely if we - // were to wait here. See https://crbug.com/616203 - }; - - /** - * Handle a MS_CONFIGURED message from the plugin. - * The plugin sends this message after successfully configuring the audio - * input - * stream. - * @private - */ - NaClManager.prototype.handleMsConfigured_ = function() { - if (this.recognizerState_ != ManagerState_.LOADING) { - return; - } - this.recognizerState_ = ManagerState_.STOPPED; - this.dispatchEvent(new Event(hotword.constants.Event.READY)); - }; - - /** - * Handle a READY_FOR_AUDIO message from the plugin. - * The plugin sends this message after the recognizer is started and - * successfully receives and processes audio data. - * @private - */ - NaClManager.prototype.handleReadyForAudio_ = function() { - if (this.recognizerState_ != ManagerState_.STARTING) { - return; - } - this.recognizerState_ = ManagerState_.RUNNING; - }; - - /** - * Handle a HOTWORD_DETECTED message from the plugin. - * The plugin sends this message after detecting the hotword. - * @private - */ - NaClManager.prototype.handleHotwordDetected_ = function() { - if (this.recognizerState_ != ManagerState_.RUNNING) { - return; - } - // We'll receive a STOPPED message very soon. - this.recognizerState_ = ManagerState_.STOPPING; - this.waitForMessage_( - hotword.constants.TimeoutMs.NORMAL, - hotword.constants.NaClPlugin.STOPPED); - var event = new Event(hotword.constants.Event.TRIGGER); - event.log = this.preambleLog_; - this.dispatchEvent(event); - }; - - /** - * Handle a STOPPED message from the plugin. - * This plugin sends this message after stopping the recognizer. This can - * happen - * either in response to a stop request, or after the hotword is detected. - * @private - */ - NaClManager.prototype.handleStopped_ = function() { - this.recognizerState_ = ManagerState_.STOPPED; - if (this.restartOnStop_) { - this.restartOnStop_ = false; - this.startRecognizer(this.startMode_); - } - }; - - /** - * Handle a TIMEOUT message from the plugin. - * The plugin sends this message when it thinks the stream is from a DSP and - * a hotword wasn't detected within a timeout period after arrival of the - * first - * audio samples. - * @private - */ - NaClManager.prototype.handleTimeout_ = function() { - if (this.recognizerState_ != ManagerState_.RUNNING) { - return; - } - this.recognizerState_ = ManagerState_.STOPPED; - this.dispatchEvent(new Event(hotword.constants.Event.TIMEOUT)); - }; - - /** - * Handle a SPEAKER_MODEL_SAVED message from the plugin. - * The plugin sends this message after writing the model to a file. - * @private - */ - NaClManager.prototype.handleSpeakerModelSaved_ = function() { - this.dispatchEvent(new Event(hotword.constants.Event.SPEAKER_MODEL_SAVED)); - }; - - /** - * Handles a message from the NaCl plugin. - * @param {!Event} msg Message from NaCl plugin. - * @private - */ - NaClManager.prototype.handlePluginMessage_ = function(msg) { - if (msg['data']) { - if (typeof(msg['data']) == 'object') { - // Save the preamble for delivery to the trigger handler when the - // trigger - // message arrives. - this.preambleLog_ = msg['data']; - return; - } - this.receivedMessage_(msg['data']); - switch (msg['data']) { - case hotword.constants.NaClPlugin.REQUEST_MODEL: - this.handleRequestModel_(); - break; - case hotword.constants.NaClPlugin.MODEL_LOADED: - this.handleModelLoaded_(); - break; - case hotword.constants.NaClPlugin.MS_CONFIGURED: - this.handleMsConfigured_(); - break; - case hotword.constants.NaClPlugin.READY_FOR_AUDIO: - this.handleReadyForAudio_(); - break; - case hotword.constants.NaClPlugin.HOTWORD_DETECTED: - this.handleHotwordDetected_(); - break; - case hotword.constants.NaClPlugin.STOPPED: - this.handleStopped_(); - break; - case hotword.constants.NaClPlugin.TIMEOUT: - this.handleTimeout_(); - break; - case hotword.constants.NaClPlugin.SPEAKER_MODEL_SAVED: - this.handleSpeakerModelSaved_(); - break; - } - } - }; - - return {NaClManager: NaClManager}; - -}); diff --git a/chromium/chrome/browser/resources/hotword/page_audio_manager.js b/chromium/chrome/browser/resources/hotword/page_audio_manager.js deleted file mode 100644 index 8c8c928cf08..00000000000 --- a/chromium/chrome/browser/resources/hotword/page_audio_manager.js +++ /dev/null @@ -1,541 +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('hotword', function() { - 'use strict'; - - /** - * Class used to manage the interaction between hotwording and the - * NTP/google.com. Injects a content script to interact with NTP/google.com - * and updates the global hotwording state based on interaction with those - * pages. - * @param {!hotword.StateManager} stateManager - * @constructor - */ - function PageAudioManager(stateManager) { - /** - * Manager of global hotwording state. - * @private {!hotword.StateManager} - */ - this.stateManager_ = stateManager; - - /** - * Mapping between tab ID and port that is connected from the injected - * content script. - * @private {!Object<number, Port>} - */ - this.portMap_ = {}; - - /** - * Chrome event listeners. Saved so that they can be de-registered when - * hotwording is disabled. - */ - this.connectListener_ = this.handleConnect_.bind(this); - this.tabCreatedListener_ = this.handleCreatedTab_.bind(this); - this.tabUpdatedListener_ = this.handleUpdatedTab_.bind(this); - this.tabActivatedListener_ = this.handleActivatedTab_.bind(this); - this.microphoneStateChangedListener_ = - this.handleMicrophoneStateChanged_.bind(this); - this.windowFocusChangedListener_ = this.handleChangedWindow_.bind(this); - this.messageListener_ = this.handleMessageFromPage_.bind(this); - - // Need to setup listeners on startup, otherwise events that caused the - // event page to start up, will be lost. - this.setupListeners_(); - - this.stateManager_.onStatusChanged.addListener(function() { - this.updateListeners_(); - this.updateTabState_(); - }.bind(this)); - } - - var CommandToPage = hotword.constants.CommandToPage; - var CommandFromPage = hotword.constants.CommandFromPage; - - PageAudioManager.prototype = { - /** - * Helper function to test if a URL path is eligible for hotwording. - * @param {!string} url URL to check. - * @param {!string} base Base URL to compare against.. - * @return {boolean} True if url is an eligible hotword URL. - * @private - */ - checkUrlPathIsEligible_: function(url, base) { - if (url == base || url == base + '/' || - url.startsWith(base + '/_/chrome/newtab?') || // Appcache NTP. - url.startsWith(base + '/?') || url.startsWith(base + '/#') || - url.startsWith(base + '/webhp') || url.startsWith(base + '/search') || - url.startsWith(base + '/imghp')) { - return true; - } - return false; - }, - - /** - * Determines if a URL is eligible for hotwording. For now, the valid pages - * are the Google HP and SERP (this will include the NTP). - * @param {!string} url URL to check. - * @return {boolean} True if url is an eligible hotword URL. - * @private - */ - isEligibleUrl_: function(url) { - if (!url) - return false; - - var baseGoogleUrls = [ - 'https://encrypted.google.', 'https://images.google.', - 'https://www.google.' - ]; - // TODO(amistry): Get this list from a file in the shared module instead. - var tlds = [ - 'at', 'ca', 'com', 'com.au', 'com.mx', 'com.br', 'co.jp', 'co.kr', - 'co.nz', 'co.uk', 'co.za', 'de', 'es', 'fr', 'it', 'ru' - ]; - - // Check for the new tab page first. - if (this.checkUrlPathIsEligible_(url, 'chrome://newtab')) - return true; - - // Check URLs with each type of local-based TLD. - for (var i = 0; i < baseGoogleUrls.length; i++) { - for (var j = 0; j < tlds.length; j++) { - var base = baseGoogleUrls[i] + tlds[j]; - if (this.checkUrlPathIsEligible_(url, base)) - return true; - } - } - return false; - }, - - /** - * Locates the current active tab in the current focused window and - * performs a callback with the tab as the parameter. - * @param {function(?Tab)} callback Function to call with the - * active tab or null if not found. The function's |this| will be set to - * this object. - * @private - */ - findCurrentTab_: function(callback) { - chrome.windows.getAll({'populate': true}, function(windows) { - for (var i = 0; i < windows.length; ++i) { - if (!windows[i].focused) - continue; - - for (var j = 0; j < windows[i].tabs.length; ++j) { - var tab = windows[i].tabs[j]; - if (tab.active) { - callback.call(this, tab); - return; - } - } - } - callback.call(this, null); - }.bind(this)); - }, - - /** - * This function is called when a tab is activated (comes into focus). - * @param {Tab} tab Current active tab. - * @private - */ - activateTab_: function(tab) { - if (!tab) { - this.stopHotwording_(); - return; - } - if (tab.id in this.portMap_) { - this.startHotwordingIfEligible_(); - return; - } - this.stopHotwording_(); - this.prepareTab_(tab); - }, - - /** - * Prepare a new or updated tab by injecting the content script. - * @param {!Tab} tab Newly updated or created tab. - * @private - */ - prepareTab_: function(tab) { - if (!this.isEligibleUrl_(tab.url)) - return; - - chrome.tabs.executeScript( - tab.id, {'file': 'audio_client.js'}, function(results) { - if (chrome.runtime.lastError) { - // Ignore this error. For new tab pages, even though the URL is - // reported to be chrome://newtab/, the actual URL is a - // country-specific google domain. Since we don't have permission - // to inject on every page, an error will happen when the user is - // in an unsupported country. - // - // The property still needs to be accessed so that the error - // condition is cleared. If it isn't, exectureScript will log an - // error the next time it is called. - } - }); - }, - - /** - * Updates hotwording state based on the state of current tabs/windows. - * @private - */ - updateTabState_: function() { - this.findCurrentTab_(this.activateTab_); - }, - - /** - * Handles a newly created tab. - * @param {!Tab} tab Newly created tab. - * @private - */ - handleCreatedTab_: function(tab) { - this.prepareTab_(tab); - }, - - /** - * Handles an updated tab. - * @param {number} tabId Id of the updated tab. - * @param {{status: string}} info Change info of the tab. - * @param {!Tab} tab Updated tab. - * @private - */ - handleUpdatedTab_: function(tabId, info, tab) { - // Chrome fires multiple update events: undefined, loading and completed. - // We perform content injection on loading state. - if (info['status'] != 'loading') - return; - - this.prepareTab_(tab); - }, - - /** - * Handles a tab that has just become active. - * @param {{tabId: number}} info Information about the activated tab. - * @private - */ - handleActivatedTab_: function(info) { - this.updateTabState_(); - }, - - /** - * Handles the microphone state changing. - * @param {boolean} enabled Whether the microphone is now enabled. - * @private - */ - handleMicrophoneStateChanged_: function(enabled) { - if (enabled) { - this.updateTabState_(); - return; - } - - this.stopHotwording_(); - }, - - /** - * Handles a change in Chrome windows. - * Note: this does not always trigger in Linux. - * @param {number} windowId Id of newly focused window. - * @private - */ - handleChangedWindow_: function(windowId) { - this.updateTabState_(); - }, - - /** - * Handles a content script attempting to connect. - * @param {!Port} port Communications port from the client. - * @private - */ - handleConnect_: function(port) { - if (port.name != hotword.constants.CLIENT_PORT_NAME) - return; - - var tab = /** @type {!Tab} */ (port.sender.tab); - // An existing port from the same tab might already exist. But that port - // may be from the previous page, so just overwrite the port. - this.portMap_[tab.id] = port; - port.onDisconnect.addListener(function() { - this.handleClientDisconnect_(port); - }.bind(this)); - port.onMessage.addListener(function(msg) { - this.handleMessage_(msg, port.sender, port.postMessage); - }.bind(this)); - }, - - /** - * Handles a client content script disconnect. - * @param {Port} port Disconnected port. - * @private - */ - handleClientDisconnect_: function(port) { - var tabId = port.sender.tab.id; - if (tabId in this.portMap_ && this.portMap_[tabId] == port) { - // Due to a race between port disconnection and tabs.onUpdated messages, - // the port could have changed. - delete this.portMap_[port.sender.tab.id]; - } - this.stopHotwordingIfIneligibleTab_(); - }, - - /** - * Disconnect all connected clients. - * @private - */ - disconnectAllClients_: function() { - for (var id in this.portMap_) { - var port = this.portMap_[id]; - port.disconnect(); - delete this.portMap_[id]; - } - }, - - /** - * Sends a command to the client content script on an eligible tab. - * @param {hotword.constants.CommandToPage} command Command to send. - * @param {number} tabId Id of the target tab. - * @private - */ - sendClient_: function(command, tabId) { - if (tabId in this.portMap_) { - var message = {}; - message[hotword.constants.COMMAND_FIELD_NAME] = command; - this.portMap_[tabId].postMessage(message); - } - }, - - /** - * Sends a command to all connected clients. - * @param {hotword.constants.CommandToPage} command Command to send. - * @private - */ - sendAllClients_: function(command) { - for (var idStr in this.portMap_) { - var id = parseInt(idStr, 10); - assert(!isNaN(id), 'Tab ID is not a number: ' + idStr); - this.sendClient_(command, id); - } - }, - - /** - * Handles a hotword trigger. Sends a trigger message to the currently - * active tab. - * @private - */ - hotwordTriggered_: function() { - this.findCurrentTab_(function(tab) { - if (tab) - this.sendClient_(CommandToPage.HOTWORD_VOICE_TRIGGER, tab.id); - }); - }, - - /** - * Starts hotwording. - * @private - */ - startHotwording_: function() { - this.stateManager_.startSession( - hotword.constants.SessionSource.NTP, function() { - this.sendAllClients_(CommandToPage.HOTWORD_STARTED); - }.bind(this), this.hotwordTriggered_.bind(this)); - }, - - /** - * Starts hotwording if the currently active tab is eligible for hotwording - * (e.g. google.com). - * @private - */ - startHotwordingIfEligible_: function() { - this.findCurrentTab_(function(tab) { - if (!tab) { - this.stopHotwording_(); - return; - } - if (this.isEligibleUrl_(tab.url)) - this.startHotwording_(); - }); - }, - - /** - * Stops hotwording. - * @private - */ - stopHotwording_: function() { - this.stateManager_.stopSession(hotword.constants.SessionSource.NTP); - this.sendAllClients_(CommandToPage.HOTWORD_ENDED); - }, - - /** - * Stops hotwording if the currently active tab is not eligible for - * hotwording (i.e. google.com). - * @private - */ - stopHotwordingIfIneligibleTab_: function() { - this.findCurrentTab_(function(tab) { - if (!tab) { - this.stopHotwording_(); - return; - } - if (!this.isEligibleUrl_(tab.url)) - this.stopHotwording_(); - }); - }, - - /** - * Handles a message from the content script injected into the page. - * @param {!Object} request Request from the content script. - * @param {!MessageSender} sender Message sender. - * @param {!function(Object)} sendResponse Function for sending a response. - * @private - */ - handleMessage_: function(request, sender, sendResponse) { - switch (request[hotword.constants.COMMAND_FIELD_NAME]) { - // TODO(amistry): Handle other messages such as CLICKED_RESUME and - // CLICKED_RESTART, if necessary. - case CommandFromPage.SPEECH_START: - this.stopHotwording_(); - break; - case CommandFromPage.SPEECH_END: - case CommandFromPage.SPEECH_RESET: - this.startHotwording_(); - break; - } - }, - - - /** - * Handles a message directly from the NTP/HP/SERP. - * @param {!Object} request Message from the sender. - * @param {!MessageSender} sender Information about the sender. - * @param {!function(HotwordStatus)} sendResponse Callback to respond - * to sender. - * @return {boolean} Whether to maintain the port open to call sendResponse. - * @private - */ - handleMessageFromPage_: function(request, sender, sendResponse) { - switch (request.type) { - case CommandFromPage.PAGE_WAKEUP: - if (sender.tab && this.isEligibleUrl_(sender.tab.url)) { - chrome.hotwordPrivate.getStatus( - true /* getOptionalFields */, - this.statusDone_.bind( - this, request.tab || sender.tab || {incognito: true}, - sendResponse)); - return true; - } - - // Do not show the opt-in promo for ineligible urls. - this.sendResponse_({'doNotShowOptinMessage': true}, sendResponse); - break; - case CommandFromPage.CLICKED_OPTIN: - chrome.hotwordPrivate.setEnabled(true); - break; - // User has explicitly clicked 'no thanks'. - case CommandFromPage.CLICKED_NO_OPTIN: - chrome.hotwordPrivate.setEnabled(false); - break; - } - return false; - }, - - /** - * Sends a message directly to the sending page. - * @param {!HotwordStatus} response The response to send to the sender. - * @param {!function(HotwordStatus)} sendResponse Callback to respond - * to sender. - * @private - */ - sendResponse_: function(response, sendResponse) { - try { - sendResponse(response); - } catch (err) { - // Suppress the exception thrown by sendResponse() when the page doesn't - // specify a response callback in the call to - // chrome.runtime.sendMessage(). - // Unfortunately, there doesn't appear to be a way to detect one-way - // messages without explicitly saying in the message itself. This - // message is defined as a constant in - // extensions/renderer/messaging_bindings.cc - if (err.message == 'Attempting to use a disconnected port object') - return; - throw err; - } - }, - - /** - * Sends the response to the tab. - * @param {Tab} tab The tab that the request was sent from. - * @param {function(HotwordStatus)} sendResponse Callback function to - * respond to sender. - * @param {HotwordStatus} hotwordStatus Status of the hotword extension. - * @private - */ - statusDone_: function(tab, sendResponse, hotwordStatus) { - var response = {'doNotShowOptinMessage': true}; - - // If always-on is available, then we do not show the promo, as the promo - // only works with the sometimes-on pref. - if (!tab.incognito && hotwordStatus.available && - !hotwordStatus.enabledSet && !hotwordStatus.alwaysOnAvailable) { - response = hotwordStatus; - } - - this.sendResponse_(response, sendResponse); - }, - - /** - * Set up event listeners. - * @private - */ - setupListeners_: function() { - if (chrome.runtime.onConnect.hasListener(this.connectListener_)) - return; - - chrome.runtime.onConnect.addListener(this.connectListener_); - chrome.tabs.onCreated.addListener(this.tabCreatedListener_); - chrome.tabs.onUpdated.addListener(this.tabUpdatedListener_); - chrome.tabs.onActivated.addListener(this.tabActivatedListener_); - chrome.windows.onFocusChanged.addListener( - this.windowFocusChangedListener_); - chrome.hotwordPrivate.onMicrophoneStateChanged.addListener( - this.microphoneStateChangedListener_); - if (chrome.runtime.onMessage.hasListener(this.messageListener_)) - return; - chrome.runtime.onMessageExternal.addListener(this.messageListener_); - }, - - /** - * Remove event listeners. - * @private - */ - removeListeners_: function() { - chrome.runtime.onConnect.removeListener(this.connectListener_); - chrome.tabs.onCreated.removeListener(this.tabCreatedListener_); - chrome.tabs.onUpdated.removeListener(this.tabUpdatedListener_); - chrome.tabs.onActivated.removeListener(this.tabActivatedListener_); - chrome.windows.onFocusChanged.removeListener( - this.windowFocusChangedListener_); - chrome.hotwordPrivate.onMicrophoneStateChanged.removeListener( - this.microphoneStateChangedListener_); - // Don't remove the Message listener, as we want them listening all - // the time, - }, - - /** - * Update event listeners based on the current hotwording state. - * @private - */ - updateListeners_: function() { - if (this.stateManager_.isSometimesOnEnabled()) { - this.setupListeners_(); - } else { - this.removeListeners_(); - this.stopHotwording_(); - this.disconnectAllClients_(); - } - } - }; - - return {PageAudioManager: PageAudioManager}; -}); diff --git a/chromium/chrome/browser/resources/hotword/state_manager.js b/chromium/chrome/browser/resources/hotword/state_manager.js deleted file mode 100644 index 98e0a5cbd17..00000000000 --- a/chromium/chrome/browser/resources/hotword/state_manager.js +++ /dev/null @@ -1,641 +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('hotword', function() { - 'use strict'; - - /** - * Trivial container class for session information. - * @param {!hotword.constants.SessionSource} source Source of the hotword - * session. - * @param {!function()} triggerCb Callback invoked when the hotword has - * triggered. - * @param {!function()} startedCb Callback invoked when the session has - * been started successfully. - * @param {function()=} opt_modelSavedCb Callback invoked when the speaker - * model has been saved successfully. - * @constructor - * @struct - * @private - */ - function Session_(source, triggerCb, startedCb, opt_modelSavedCb) { - /** - * Source of the hotword session request. - * @private {!hotword.constants.SessionSource} - */ - this.source_ = source; - - /** - * Callback invoked when the hotword has triggered. - * @private {!function()} - */ - this.triggerCb_ = triggerCb; - - /** - * Callback invoked when the session has been started successfully. - * @private {?function()} - */ - this.startedCb_ = startedCb; - - /** - * Callback invoked when the session has been started successfully. - * @private {?function()} - */ - this.speakerModelSavedCb_ = opt_modelSavedCb; - } - - /** - * Class to manage hotwording state. Starts/stops the hotword detector based - * on user settings, session requests, and any other factors that play into - * whether or not hotwording should be running. - * @constructor - */ - function StateManager() { - /** - * Current state. - * @private {hotword.StateManager.State_} - */ - this.state_ = State_.STOPPED; - - /** - * Current hotwording status. - * @private {?chrome.hotwordPrivate.StatusDetails} - */ - this.hotwordStatus_ = null; - - /** - * NaCl plugin manager. - * @private {?hotword.NaClManager} - */ - this.pluginManager_ = null; - - /** - * Currently active hotwording sessions. - * @private {!Array<Session_>} - */ - this.sessions_ = []; - - /** - * The mode to start the recognizer in. - * @private {!hotword.constants.RecognizerStartMode} - */ - this.startMode_ = hotword.constants.RecognizerStartMode.NORMAL; - - /** - * Event that fires when the hotwording status has changed. - * @type {!ChromeEvent} - */ - this.onStatusChanged = new chrome.Event(); - - /** - * Hotword trigger audio notification... a.k.a The Chime (tm). - * @private {!HTMLAudioElement} - */ - this.chime_ = - /** @type {!HTMLAudioElement} */ (document.createElement('audio')); - - /** - * Chrome event listeners. Saved so that they can be de-registered when - * hotwording is disabled. - * @private - */ - this.idleStateChangedListener_ = this.handleIdleStateChanged_.bind(this); - this.startupListener_ = this.handleStartup_.bind(this); - - /** - * Whether this user is locked. - * @private {boolean} - */ - this.isLocked_ = false; - - /** - * Current state of audio logging. - * This is tracked separately from hotwordStatus_ because we need to restart - * the hotword detector when this value changes. - * @private {boolean} - */ - this.loggingEnabled_ = false; - - /** - * Current state of training. - * This is tracked separately from |hotwordStatus_| because we need to - * restart the hotword detector when this value changes. - * @private {!boolean} - */ - this.trainingEnabled_ = false; - - /** - * Helper class to keep this extension alive while the hotword detector is - * running in always-on mode. - * @private {!hotword.KeepAlive} - */ - this.keepAlive_ = new hotword.KeepAlive(); - - // Get the initial status. - chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this)); - - // Setup the chime and insert into the page. - // Set preload=none to prevent an audio output stream from being created - // when the extension loads. - this.chime_.preload = 'none'; - this.chime_.src = chrome.extension.getURL( - hotword.constants.SHARED_MODULE_ROOT + '/audio/chime.wav'); - document.body.appendChild(this.chime_); - - // In order to remove this listener, it must first be added. This handles - // the case on first Chrome startup where this event is never registered, - // so can't be removed when it's determined that hotwording is disabled. - // Why not only remove the listener if it exists? Extension API events have - // two parts to them, the Javascript listeners, and a browser-side component - // that wakes up the extension if it's an event page. The browser-side - // wake-up event is only removed when the number of javascript listeners - // becomes 0. To clear the browser wake-up event, a listener first needs to - // be added, then removed in order to drop the count to 0 and remove the - // event. - chrome.runtime.onStartup.addListener(this.startupListener_); - } - - /** - * @enum {number} - * @private - */ - StateManager.State_ = { - STOPPED: 0, - STARTING: 1, - RUNNING: 2, - ERROR: 3, - }; - var State_ = StateManager.State_; - - var UmaMediaStreamOpenResults_ = { - // These first error are defined by the MediaStream spec: - // http://w3c.github.io/mediacapture-main/getusermedia.html#idl-def-MediaStreamError - 'NotSupportedError': - hotword.constants.UmaMediaStreamOpenResult.NOT_SUPPORTED, - 'PermissionDeniedError': - hotword.constants.UmaMediaStreamOpenResult.PERMISSION_DENIED, - 'ConstraintNotSatisfiedError': - hotword.constants.UmaMediaStreamOpenResult.CONSTRAINT_NOT_SATISFIED, - 'OverconstrainedError': - hotword.constants.UmaMediaStreamOpenResult.OVERCONSTRAINED, - 'NotFoundError': hotword.constants.UmaMediaStreamOpenResult.NOT_FOUND, - 'AbortError': hotword.constants.UmaMediaStreamOpenResult.ABORT, - 'SourceUnavailableError': - hotword.constants.UmaMediaStreamOpenResult.SOURCE_UNAVAILABLE, - // The next few errors are chrome-specific. See: - // content/renderer/media/user_media_client_impl.cc - // (UserMediaClientImpl::GetUserMediaRequestFailed) - 'PermissionDismissedError': - hotword.constants.UmaMediaStreamOpenResult.PERMISSION_DISMISSED, - 'InvalidStateError': - hotword.constants.UmaMediaStreamOpenResult.INVALID_STATE, - 'DevicesNotFoundError': - hotword.constants.UmaMediaStreamOpenResult.DEVICES_NOT_FOUND, - 'InvalidSecurityOriginError': - hotword.constants.UmaMediaStreamOpenResult.INVALID_SECURITY_ORIGIN - }; - - var UmaTriggerSources_ = { - 'launcher': hotword.constants.UmaTriggerSource.LAUNCHER, - 'ntp': hotword.constants.UmaTriggerSource.NTP_GOOGLE_COM, - 'always': hotword.constants.UmaTriggerSource.ALWAYS_ON, - 'training': hotword.constants.UmaTriggerSource.TRAINING - }; - - StateManager.prototype = { - /** - * Request status details update. Intended to be called from the - * hotwordPrivate.onEnabledChanged() event. - */ - updateStatus: function() { - chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this)); - }, - - /** - * @return {boolean} True if google.com/NTP/launcher hotwording is enabled. - */ - isSometimesOnEnabled: function() { - assert( - this.hotwordStatus_, 'No hotwording status (isSometimesOnEnabled)'); - // Although the two settings are supposed to be mutually exclusive, it's - // possible for both to be set. In that case, always-on takes precedence. - return this.hotwordStatus_.enabled && - !this.hotwordStatus_.alwaysOnEnabled; - }, - - /** - * @return {boolean} True if always-on hotwording is enabled. - */ - isAlwaysOnEnabled: function() { - assert(this.hotwordStatus_, 'No hotword status (isAlwaysOnEnabled)'); - return this.hotwordStatus_.alwaysOnEnabled && - !this.hotwordStatus_.trainingEnabled; - }, - - /** - * @return {boolean} True if training is enabled. - */ - isTrainingEnabled: function() { - assert(this.hotwordStatus_, 'No hotword status (isTrainingEnabled)'); - return this.hotwordStatus_.trainingEnabled; - }, - - /** - * Callback for hotwordPrivate.getStatus() function. - * @param {chrome.hotwordPrivate.StatusDetails} status Current hotword - * status. - * @private - */ - handleStatus_: function(status) { - hotword.debug('New hotword status', status); - this.hotwordStatus_ = status; - this.updateStateFromStatus_(); - - this.onStatusChanged.dispatch(); - }, - - /** - * Updates state based on the current status. - * @private - */ - updateStateFromStatus_: function() { - if (!this.hotwordStatus_) - return; - - if (this.hotwordStatus_.enabled || this.hotwordStatus_.alwaysOnEnabled || - this.hotwordStatus_.trainingEnabled) { - // Detect changes to audio logging and kill the detector if that setting - // has changed. - if (this.hotwordStatus_.audioLoggingEnabled != this.loggingEnabled_) - this.shutdownDetector_(); - this.loggingEnabled_ = this.hotwordStatus_.audioLoggingEnabled; - - // If the training state has changed, we need to first shut down the - // detector so that we can restart in a different mode. - if (this.hotwordStatus_.trainingEnabled != this.trainingEnabled_) - this.shutdownDetector_(); - this.trainingEnabled_ = this.hotwordStatus_.trainingEnabled; - - // Start the detector if there's a session and the user is unlocked, and - // stops it otherwise. - if (this.sessions_.length && !this.isLocked_ && - this.hotwordStatus_.userIsActive) { - this.startDetector_(); - } else { - this.shutdownDetector_(); - } - - if (!chrome.idle.onStateChanged.hasListener( - this.idleStateChangedListener_)) { - chrome.idle.onStateChanged.addListener( - this.idleStateChangedListener_); - } - if (!chrome.runtime.onStartup.hasListener(this.startupListener_)) - chrome.runtime.onStartup.addListener(this.startupListener_); - } else { - // Not enabled. Shut down if running. - this.shutdownDetector_(); - - chrome.idle.onStateChanged.removeListener( - this.idleStateChangedListener_); - // If hotwording isn't enabled, don't start this component extension on - // Chrome startup. If a user enables hotwording, the status change - // event will be fired and the onStartup event will be registered. - chrome.runtime.onStartup.removeListener(this.startupListener_); - } - }, - - /** - * Starts the hotword detector. - * @private - */ - startDetector_: function() { - // Last attempt to start detector resulted in an error. - if (this.state_ == State_.ERROR) { - // TODO(amistry): Do some error rate tracking here and disable the - // extension if we error too often. - } - - if (!this.pluginManager_) { - this.state_ = State_.STARTING; - var isHotwordStream = this.isAlwaysOnEnabled() && - this.hotwordStatus_.hotwordHardwareAvailable; - this.pluginManager_ = - new hotword.NaClManager(this.loggingEnabled_, isHotwordStream); - this.pluginManager_.addEventListener( - hotword.constants.Event.READY, this.onReady_.bind(this)); - this.pluginManager_.addEventListener( - hotword.constants.Event.ERROR, this.onError_.bind(this)); - this.pluginManager_.addEventListener( - hotword.constants.Event.TRIGGER, this.onTrigger_.bind(this)); - this.pluginManager_.addEventListener( - hotword.constants.Event.TIMEOUT, this.onTimeout_.bind(this)); - this.pluginManager_.addEventListener( - hotword.constants.Event.SPEAKER_MODEL_SAVED, - this.onSpeakerModelSaved_.bind(this)); - chrome.runtime.getPlatformInfo(function(platform) { - var naclArch = platform.nacl_arch; - - // googDucking set to false so that audio output level from other tabs - // is not affected when hotword is enabled. https://crbug.com/357773 - // content/common/media/media_stream_options.cc - // When always-on is enabled, request the hotword stream. - // Optional because we allow power users to bypass the hardware - // detection via a flag, and hence the hotword stream may not be - // available. - var constraints = /** @type {googMediaStreamConstraints} */ - ({ - audio: { - optional: [ - {googDucking: false}, - {googHotword: this.isAlwaysOnEnabled()} - ] - } - }); - navigator.webkitGetUserMedia( - /** @type {MediaStreamConstraints} */ (constraints), - function(stream) { - hotword.metrics.recordEnum( - hotword.constants.UmaMetrics.MEDIA_STREAM_RESULT, - hotword.constants.UmaMediaStreamOpenResult.SUCCESS, - hotword.constants.UmaMediaStreamOpenResult.MAX); - // The detector could have been shut down before the stream - // finishes opening. - if (this.pluginManager_ == null) { - stream.getAudioTracks()[0].stop(); - return; - } - - if (this.isAlwaysOnEnabled()) - this.keepAlive_.start(); - if (!this.pluginManager_.initialize(naclArch, stream)) { - this.state_ = State_.ERROR; - this.shutdownPluginManager_(); - } - }.bind(this), - function(error) { - if (error.name in UmaMediaStreamOpenResults_) { - var metricValue = UmaMediaStreamOpenResults_[error.name]; - } else { - var metricValue = - hotword.constants.UmaMediaStreamOpenResult.UNKNOWN; - } - hotword.metrics.recordEnum( - hotword.constants.UmaMetrics.MEDIA_STREAM_RESULT, - metricValue, - hotword.constants.UmaMediaStreamOpenResult.MAX); - this.state_ = State_.ERROR; - this.pluginManager_ = null; - }.bind(this)); - }.bind(this)); - } else if (this.state_ != State_.STARTING) { - // Don't try to start a starting detector. - this.startRecognizer_(); - } - }, - - /** - * Start the recognizer plugin. Assumes the plugin has been loaded and is - * ready to start. - * @private - */ - startRecognizer_: function() { - assert(this.pluginManager_, 'No NaCl plugin loaded'); - if (this.state_ != State_.RUNNING) { - this.state_ = State_.RUNNING; - if (this.isAlwaysOnEnabled()) - this.keepAlive_.start(); - this.pluginManager_.startRecognizer(this.startMode_); - } - for (var i = 0; i < this.sessions_.length; i++) { - var session = this.sessions_[i]; - if (session.startedCb_) { - session.startedCb_(); - session.startedCb_ = null; - } - } - }, - - /** - * Stops the hotword detector, if it's running. - * @private - */ - stopDetector_: function() { - this.keepAlive_.stop(); - if (this.pluginManager_ && this.state_ == State_.RUNNING) { - this.state_ = State_.STOPPED; - this.pluginManager_.stopRecognizer(); - } - }, - - /** - * Shuts down and removes the plugin manager, if it exists. - * @private - */ - shutdownPluginManager_: function() { - this.keepAlive_.stop(); - if (this.pluginManager_) { - this.pluginManager_.shutdown(); - this.pluginManager_ = null; - } - }, - - /** - * Shuts down the hotword detector. - * @private - */ - shutdownDetector_: function() { - this.state_ = State_.STOPPED; - this.shutdownPluginManager_(); - }, - - /** - * Finalizes the speaker model. Assumes the plugin has been loaded and - * started. - */ - finalizeSpeakerModel: function() { - assert( - this.pluginManager_, - 'Cannot finalize speaker model: No NaCl plugin loaded'); - if (this.state_ != State_.RUNNING) { - hotword.debug('Cannot finalize speaker model: NaCl plugin not started'); - return; - } - this.pluginManager_.finalizeSpeakerModel(); - }, - - /** - * Handle the hotword plugin being ready to start. - * @private - */ - onReady_: function() { - if (this.state_ != State_.STARTING) { - // At this point, we should not be in the RUNNING state. Doing so would - // imply the hotword detector was started without being ready. - assert(this.state_ != State_.RUNNING, 'Unexpected RUNNING state'); - this.shutdownPluginManager_(); - return; - } - this.startRecognizer_(); - }, - - /** - * Handle an error from the hotword plugin. - * @private - */ - onError_: function() { - this.state_ = State_.ERROR; - this.shutdownPluginManager_(); - }, - - /** - * Handle hotword triggering. - * @param {!Event} event Event containing audio log data. - * @private - */ - onTrigger_: function(event) { - this.keepAlive_.stop(); - hotword.debug('Hotword triggered!'); - chrome.metricsPrivate.recordUserAction( - hotword.constants.UmaMetrics.TRIGGER); - assert(this.pluginManager_, 'No NaCl plugin loaded on trigger'); - // Detector implicitly stops when the hotword is detected. - this.state_ = State_.STOPPED; - - // Play the chime. - this.chime_.play(); - - // Implicitly clear the top session. A session needs to be started in - // order to restart the detector. - if (this.sessions_.length) { - var session = this.sessions_.pop(); - session.triggerCb_(event.log); - - hotword.metrics.recordEnum( - hotword.constants.UmaMetrics.TRIGGER_SOURCE, - UmaTriggerSources_[session.source_], - hotword.constants.UmaTriggerSource.MAX); - } - - // If we're in always-on mode, shut down the hotword detector. The hotword - // stream requires that we close and re-open it after a trigger, and the - // only way to accomplish this is to shut everything down. - if (this.isAlwaysOnEnabled()) - this.shutdownDetector_(); - }, - - /** - * Handle hotword timeout. - * @private - */ - onTimeout_: function() { - hotword.debug('Hotword timeout!'); - - // We get this event when the hotword detector thinks there's a false - // trigger. In this case, we need to shut down and restart the detector to - // re-arm the DSP. - this.shutdownDetector_(); - this.updateStateFromStatus_(); - }, - - /** - * Handle speaker model saved. - * @private - */ - onSpeakerModelSaved_: function() { - hotword.debug('Speaker model saved!'); - - if (this.sessions_.length) { - // Only call the callback of the the top session. - var session = this.sessions_[this.sessions_.length - 1]; - if (session.speakerModelSavedCb_) - session.speakerModelSavedCb_(); - } - }, - - /** - * Remove a hotwording session from the given source. - * @param {!hotword.constants.SessionSource} source Source of the hotword - * session request. - * @private - */ - removeSession_: function(source) { - for (var i = 0; i < this.sessions_.length; i++) { - if (this.sessions_[i].source_ == source) { - this.sessions_.splice(i, 1); - break; - } - } - }, - - /** - * Start a hotwording session. - * @param {!hotword.constants.SessionSource} source Source of the hotword - * session request. - * @param {!function()} startedCb Callback invoked when the session has - * been started successfully. - * @param {!function()} triggerCb Callback invoked when the hotword has - * @param {function()=} modelSavedCb Callback invoked when the speaker model - * has been saved. - * @param {hotword.constants.RecognizerStartMode=} opt_mode The mode to - * start the recognizer in. - */ - startSession: function( - source, startedCb, triggerCb, opt_modelSavedCb, opt_mode) { - if (this.isTrainingEnabled() && opt_mode) { - this.startMode_ = opt_mode; - } else { - this.startMode_ = hotword.constants.RecognizerStartMode.NORMAL; - } - hotword.debug('Starting session for source: ' + source); - this.removeSession_(source); - this.sessions_.push( - new Session_(source, triggerCb, startedCb, opt_modelSavedCb)); - this.updateStateFromStatus_(); - }, - - /** - * Stops a hotwording session. - * @param {!hotword.constants.SessionSource} source Source of the hotword - * session request. - */ - stopSession: function(source) { - hotword.debug('Stopping session for source: ' + source); - this.removeSession_(source); - // If this is a training session then switch the start mode back to - // normal. - if (source == hotword.constants.SessionSource.TRAINING) - this.startMode_ = hotword.constants.RecognizerStartMode.NORMAL; - this.updateStateFromStatus_(); - }, - - /** - * Handles a chrome.idle.onStateChanged event. - * @param {!string} state State, one of "active", "idle", or "locked". - * @private - */ - handleIdleStateChanged_: function(state) { - hotword.debug('Idle state changed: ' + state); - var oldLocked = this.isLocked_; - if (state == 'locked') - this.isLocked_ = true; - else - this.isLocked_ = false; - - if (oldLocked != this.isLocked_) - this.updateStateFromStatus_(); - }, - - /** - * Handles a chrome.runtime.onStartup event. - * @private - */ - handleStartup_: function() { - // Nothing specific needs to be done here. This function exists solely to - // be registered on the startup event. - } - }; - - return {StateManager: StateManager}; -}); diff --git a/chromium/chrome/browser/resources/hotword/training_manager.js b/chromium/chrome/browser/resources/hotword/training_manager.js deleted file mode 100644 index f5b68d44885..00000000000 --- a/chromium/chrome/browser/resources/hotword/training_manager.js +++ /dev/null @@ -1,201 +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('hotword', function() { - 'use strict'; - - /** - * Class used to manage speaker training. Starts a hotwording session - * if training is on, and automatically restarts the detector when a - * a hotword is triggered. - * @param {!hotword.StateManager} stateManager - * @constructor - * @extends {hotword.BaseSessionManager} - */ - function TrainingManager(stateManager) { - /** - * Chrome event listeners. Saved so that they can be de-registered when - * hotwording is disabled. - * @private - */ - this.finalizedSpeakerModelListener_ = - this.handleFinalizeSpeakerModel_.bind(this); - - hotword.BaseSessionManager.call( - this, stateManager, hotword.constants.SessionSource.TRAINING); - } - - /** - * Handles a success event on mounting the file system event and deletes - * the user data files. - * @param {FileSystem} fs The FileSystem object. - * @private - */ - TrainingManager.deleteFiles_ = function(fs) { - fs.root.getFile( - hotword.constants.SPEAKER_MODEL_FILE_NAME, {create: false}, - TrainingManager.deleteFile_, TrainingManager.fileErrorHandler_); - - for (var i = 0; i < hotword.constants.NUM_TRAINING_UTTERANCES; ++i) { - fs.root.getFile( - hotword.constants.UTTERANCE_FILE_PREFIX + i + - hotword.constants.UTTERANCE_FILE_EXTENSION, - {create: false}, TrainingManager.deleteFile_, - TrainingManager.fileErrorHandler_); - } - }; - - /** - * Deletes a file. - * @param {FileEntry} fileEntry The FileEntry object. - * @private - */ - TrainingManager.deleteFile_ = function(fileEntry) { - if (fileEntry.isFile) { - hotword.debug('File found: ' + fileEntry.fullPath); - if (hotword.DEBUG || window.localStorage['hotword.DEBUG']) { - fileEntry.getMetadata(function(md) { - hotword.debug('File size: ' + md.size); - }); - } - fileEntry.remove(function() { - hotword.debug('File removed: ' + fileEntry.fullPath); - }, TrainingManager.fileErrorHandler_); - } - }; - - /** - * Handles a failure event on mounting the file system event. - * @param {FileError} e The FileError object. - * @private - */ - TrainingManager.fileErrorHandler_ = function(e) { - hotword.debug('File error: ' + e.code); - }; - - /** - * Handles a failure event on checking for the existence of the speaker model. - * @param {FileError} e The FileError object. - * @private - */ - TrainingManager.sendNoSpeakerModelResponse_ = function(e) { - chrome.hotwordPrivate.speakerModelExistsResult(false); - }; - - /** - * Handles a success event on mounting the file system and checks for the - * existence of the speaker model. - * @param {FileSystem} fs The FileSystem object. - * @private - */ - TrainingManager.speakerModelExists_ = function(fs) { - fs.root.getFile( - hotword.constants.SPEAKER_MODEL_FILE_NAME, {create: false}, - TrainingManager.sendSpeakerModelExistsResponse_, - TrainingManager.sendNoSpeakerModelResponse_); - }; - - /** - * Sends a response through the HotwordPrivateApi indicating whether - * the speaker model exists. - * @param {FileEntry} fileEntry The FileEntry object. - * @private - */ - TrainingManager.sendSpeakerModelExistsResponse_ = function(fileEntry) { - if (fileEntry.isFile) { - hotword.debug('File found: ' + fileEntry.fullPath); - if (hotword.DEBUG || window.localStorage['hotword.DEBUG']) { - fileEntry.getMetadata(function(md) { - hotword.debug('File size: ' + md.size); - }); - } - } - chrome.hotwordPrivate.speakerModelExistsResult(fileEntry.isFile); - }; - - /** - * Handles a request to delete the speaker model. - */ - TrainingManager.handleDeleteSpeakerModel = function() { - window.webkitRequestFileSystem( - PERSISTENT, hotword.constants.FILE_SYSTEM_SIZE_BYTES, - TrainingManager.deleteFiles_, TrainingManager.fileErrorHandler_); - }; - - /** - * Handles a request for the speaker model existence. - */ - TrainingManager.handleSpeakerModelExists = function() { - window.webkitRequestFileSystem( - PERSISTENT, hotword.constants.FILE_SYSTEM_SIZE_BYTES, - TrainingManager.speakerModelExists_, TrainingManager.fileErrorHandler_); - }; - - TrainingManager.prototype = { - __proto__: hotword.BaseSessionManager.prototype, - - /** @override */ - enabled: function() { - return this.stateManager.isTrainingEnabled(); - }, - - /** @override */ - updateListeners: function() { - hotword.BaseSessionManager.prototype.updateListeners.call(this); - - if (this.enabled()) { - // Detect when the speaker model needs to be finalized. - if (!chrome.hotwordPrivate.onFinalizeSpeakerModel.hasListener( - this.finalizedSpeakerModelListener_)) { - chrome.hotwordPrivate.onFinalizeSpeakerModel.addListener( - this.finalizedSpeakerModelListener_); - } - this.startSession(hotword.constants.RecognizerStartMode.NEW_MODEL); - } else { - chrome.hotwordPrivate.onFinalizeSpeakerModel.removeListener( - this.finalizedSpeakerModelListener_); - } - }, - - /** @override */ - handleHotwordTrigger: function(log) { - if (this.enabled()) { - hotword.BaseSessionManager.prototype.handleHotwordTrigger.call( - this, log); - this.startSession(hotword.constants.RecognizerStartMode.ADAPT_MODEL); - } - }, - - /** @override */ - startSession: function(opt_mode) { - this.stateManager.startSession( - this.sessionSource_, - function() { - chrome.hotwordPrivate.setHotwordSessionState(true, function() {}); - }, - this.handleHotwordTrigger.bind(this), - this.handleSpeakerModelSaved_.bind(this), opt_mode); - }, - - /** - * Handles a hotwordPrivate.onFinalizeSpeakerModel event. - * @private - */ - handleFinalizeSpeakerModel_: function() { - if (this.enabled()) - this.stateManager.finalizeSpeakerModel(); - }, - - /** - * Handles a hotwordPrivate.onFinalizeSpeakerModel event. - * @private - */ - handleSpeakerModelSaved_: function() { - if (this.enabled()) - chrome.hotwordPrivate.notifySpeakerModelSaved(); - }, - }; - - return {TrainingManager: TrainingManager}; -}); diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/OWNERS b/chromium/chrome/browser/resources/hotword_audio_verification/OWNERS deleted file mode 100644 index 99e28241baa..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/OWNERS +++ /dev/null @@ -1 +0,0 @@ -kcarattini@chromium.org diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/event_page.js b/chromium/chrome/browser/resources/hotword_audio_verification/event_page.js deleted file mode 100644 index ea379d33d9b..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/event_page.js +++ /dev/null @@ -1,27 +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. - -var appId = 'hotword_audio_verification'; - -chrome.app.runtime.onLaunched.addListener(function() { - // We need to focus the window if it already exists, since it - // is created as 'hidden'. - // - // Note: If we ever launch on another platform, make sure that this works - // with window managers that support hiding (e.g. Cmd+h on an app window on - // Mac). - var appWindow = chrome.app.window.get(appId); - if (appWindow) { - appWindow.focus(); - return; - } - - chrome.app.window.create('main.html', { - 'frame': 'none', - 'resizable': false, - 'hidden': true, - 'id': appId, - 'innerBounds': {'width': 784, 'height': 448} - }); -}); diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/flow.js b/chromium/chrome/browser/resources/hotword_audio_verification/flow.js deleted file mode 100644 index c22211492c4..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/flow.js +++ /dev/null @@ -1,566 +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. - -(function() { - -// Correspond to steps in the hotword opt-in flow. -/** @const */ var START = 'start-container'; -/** @const */ var AUDIO_HISTORY = 'audio-history-container'; -/** @const */ var SPEECH_TRAINING = 'speech-training-container'; -/** @const */ var FINISH = 'finish-container'; - -/** - * These flows correspond to the three LaunchModes as defined in - * chrome/browser/search/hotword_service.h and should be kept in sync - * with them. - * @const - */ -var FLOWS = [ - [START, SPEECH_TRAINING, FINISH], - [START, AUDIO_HISTORY, SPEECH_TRAINING, FINISH], [SPEECH_TRAINING, FINISH] -]; - -/** - * The launch mode. This enum needs to be kept in sync with that of - * the same name in hotword_service.h. - * @enum {number} - */ -var LaunchMode = {HOTWORD_ONLY: 0, HOTWORD_AND_AUDIO_HISTORY: 1, RETRAIN: 2}; - -/** - * The training state. - * @enum {string} - */ -var TrainingState = { - RESET: 'reset', - TIMEOUT: 'timeout', - ERROR: 'error', -}; - -/** - * Class to control the page flow of the always-on hotword and - * Audio History opt-in process. - * @constructor - */ -function Flow() { - this.currentStepIndex_ = -1; - this.currentFlow_ = []; - - /** - * The mode that this app was launched in. - * @private {LaunchMode} - */ - this.launchMode_ = LaunchMode.HOTWORD_AND_AUDIO_HISTORY; - - /** - * Whether this flow is currently in the process of training a voice model. - * @private {boolean} - */ - this.training_ = false; - - /** - * The current training state. - * @private {?TrainingState} - */ - this.trainingState_ = null; - - /** - * Whether an expected hotword trigger has been received, indexed by - * training step. - * @private {boolean[]} - */ - this.hotwordTriggerReceived_ = []; - - /** - * Prefix of the element ids for the page that is currently training. - * @private {string} - */ - this.trainingPagePrefix_ = 'speech-training'; - - /** - * Whether the speaker model for this flow has been finalized. - * @private {boolean} - */ - this.speakerModelFinalized_ = false; - - /** - * ID of the currently active timeout. - * @private {?number} - */ - this.timeoutId_ = null; - - /** - * Listener for the speakerModelSaved event. - * @private {Function} - */ - this.speakerModelFinalizedListener_ = - this.onSpeakerModelFinalized_.bind(this); - - /** - * Listener for the hotword trigger event. - * @private {Function} - */ - this.hotwordTriggerListener_ = this.handleHotwordTrigger_.bind(this); - - // Listen for the user locking the screen. - chrome.idle.onStateChanged.addListener( - this.handleIdleStateChanged_.bind(this)); - - // Listen for hotword settings changes. This used to detect when the user - // switches to a different profile. - if (chrome.hotwordPrivate.onEnabledChanged) { - chrome.hotwordPrivate.onEnabledChanged.addListener( - this.handleEnabledChanged_.bind(this)); - } -} - -/** - * Advances the current step. Begins training if the speech-training - * page has been reached. - */ -Flow.prototype.advanceStep = function() { - this.currentStepIndex_++; - if (this.currentStepIndex_ < this.currentFlow_.length) { - if (this.currentFlow_[this.currentStepIndex_] == SPEECH_TRAINING) - this.startTraining(); - this.showStep_.apply(this); - } -}; - -/** - * Gets the appropriate flow and displays its first page. - */ -Flow.prototype.startFlow = function() { - if (chrome.hotwordPrivate && chrome.hotwordPrivate.getLaunchState) - chrome.hotwordPrivate.getLaunchState(this.startFlowForMode_.bind(this)); -}; - -/** - * Starts the training process. - */ -Flow.prototype.startTraining = function() { - // Don't start a training session if one already exists. - if (this.training_) - return; - - this.training_ = true; - - if (chrome.hotwordPrivate.onHotwordTriggered && - !chrome.hotwordPrivate.onHotwordTriggered.hasListener( - this.hotwordTriggerListener_)) { - chrome.hotwordPrivate.onHotwordTriggered.addListener( - this.hotwordTriggerListener_); - } - - this.waitForHotwordTrigger_(0); - if (chrome.hotwordPrivate.startTraining) - chrome.hotwordPrivate.startTraining(); -}; - -/** - * Stops the training process. - */ -Flow.prototype.stopTraining = function() { - if (!this.training_) - return; - - this.training_ = false; - if (chrome.hotwordPrivate.onHotwordTriggered) { - chrome.hotwordPrivate.onHotwordTriggered.removeListener( - this.hotwordTriggerListener_); - } - if (chrome.hotwordPrivate.stopTraining) - chrome.hotwordPrivate.stopTraining(); -}; - -/** - * Attempts to enable audio history for the signed-in account. - */ -Flow.prototype.enableAudioHistory = function() { - // Update UI - $('audio-history-agree').disabled = true; - $('audio-history-cancel').disabled = true; - - $('audio-history-error').hidden = true; - $('audio-history-wait').hidden = false; - - if (chrome.hotwordPrivate.setAudioHistoryEnabled) { - chrome.hotwordPrivate.setAudioHistoryEnabled( - true, this.onAudioHistoryRequestCompleted_.bind(this)); - } -}; - -// ---- private methods: - -/** - * Shows an error if the audio history setting was not enabled successfully. - * @private - */ -Flow.prototype.handleAudioHistoryError_ = function() { - $('audio-history-agree').disabled = false; - $('audio-history-cancel').disabled = false; - - $('audio-history-wait').hidden = true; - $('audio-history-error').hidden = false; - - // Set a timeout before focusing the Enable button so that screenreaders - // have time to announce the error first. - this.setTimeout_(function() { - $('audio-history-agree').focus(); - }.bind(this), 50); -}; - -/** - * Callback for when an audio history request completes. - * @param {chrome.hotwordPrivate.AudioHistoryState} state The audio history - * request state. - * @private - */ -Flow.prototype.onAudioHistoryRequestCompleted_ = function(state) { - if (!state.success || !state.enabled) { - this.handleAudioHistoryError_(); - return; - } - - this.advanceStep(); -}; - -/** - * Shows an error if the speaker model has not been finalized. - * @private - */ -Flow.prototype.handleSpeakerModelFinalizedError_ = function() { - if (!this.training_) - return; - - if (this.speakerModelFinalized_) - return; - - this.updateTrainingState_(TrainingState.ERROR); - this.stopTraining(); -}; - -/** - * Handles the speaker model finalized event. - * @private - */ -Flow.prototype.onSpeakerModelFinalized_ = function() { - this.speakerModelFinalized_ = true; - if (chrome.hotwordPrivate.onSpeakerModelSaved) { - chrome.hotwordPrivate.onSpeakerModelSaved.removeListener( - this.speakerModelFinalizedListener_); - } - this.stopTraining(); - this.setTimeout_(this.finishFlow_.bind(this), 2000); -}; - -/** - * Completes the training process. - * @private - */ -Flow.prototype.finishFlow_ = function() { - if (chrome.hotwordPrivate.setHotwordAlwaysOnSearchEnabled) { - chrome.hotwordPrivate.setHotwordAlwaysOnSearchEnabled( - true, this.advanceStep.bind(this)); - } -}; - -/** - * Handles a user clicking on the retry button. - */ -Flow.prototype.handleRetry = function() { - if (!(this.trainingState_ == TrainingState.TIMEOUT || - this.trainingState_ == TrainingState.ERROR)) - return; - - this.startTraining(); - this.updateTrainingState_(TrainingState.RESET); -}; - -// ---- private methods: - -/** - * Completes the training process. - * @private - */ -Flow.prototype.finalizeSpeakerModel_ = function() { - if (!this.training_) - return; - - // Listen for the success event from the NaCl module. - if (chrome.hotwordPrivate.onSpeakerModelSaved && - !chrome.hotwordPrivate.onSpeakerModelSaved.hasListener( - this.speakerModelFinalizedListener_)) { - chrome.hotwordPrivate.onSpeakerModelSaved.addListener( - this.speakerModelFinalizedListener_); - } - - this.speakerModelFinalized_ = false; - this.setTimeout_(this.handleSpeakerModelFinalizedError_.bind(this), 30000); - if (chrome.hotwordPrivate.finalizeSpeakerModel) - chrome.hotwordPrivate.finalizeSpeakerModel(); -}; - -/** - * Returns the current training step. - * @param {string} curStepClassName The name of the class of the current - * training step. - * @return {Object} The current training step, its index, and an array of - * all training steps. Any of these can be undefined. - * @private - */ -Flow.prototype.getCurrentTrainingStep_ = function(curStepClassName) { - var steps = - $(this.trainingPagePrefix_ + '-training').querySelectorAll('.train'); - var curStep = - $(this.trainingPagePrefix_ + '-training').querySelector('.listening'); - - return { - current: curStep, - index: Array.prototype.indexOf.call(steps, curStep), - steps: steps - }; -}; - -/** - * Updates the training state. - * @param {TrainingState} state The training state. - * @private - */ -Flow.prototype.updateTrainingState_ = function(state) { - this.trainingState_ = state; - this.updateErrorUI_(); -}; - -/** - * Waits two minutes and then checks for a training error. - * @param {number} index The index of the training step. - * @private - */ -Flow.prototype.waitForHotwordTrigger_ = function(index) { - if (!this.training_) - return; - - this.hotwordTriggerReceived_[index] = false; - this.setTimeout_(this.handleTrainingTimeout_.bind(this, index), 120000); -}; - -/** - * Checks for and handles a training error. - * @param {number} index The index of the training step. - * @private - */ -Flow.prototype.handleTrainingTimeout_ = function(index) { - if (this.hotwordTriggerReceived_[index]) - return; - - this.timeoutTraining_(); -}; - -/** - * Times out training and updates the UI to show a "retry" message, if - * currently training. - * @private - */ -Flow.prototype.timeoutTraining_ = function() { - if (!this.training_) - return; - - this.clearTimeout_(); - this.updateTrainingState_(TrainingState.TIMEOUT); - this.stopTraining(); -}; - -/** - * Sets a timeout. If any timeout is active, clear it. - * @param {Function} func The function to invoke when the timeout occurs. - * @param {number} delay Timeout delay in milliseconds. - * @private - */ -Flow.prototype.setTimeout_ = function(func, delay) { - this.clearTimeout_(); - this.timeoutId_ = setTimeout(function() { - this.timeoutId_ = null; - func(); - }, delay); -}; - -/** - * Clears any currently active timeout. - * @private - */ -Flow.prototype.clearTimeout_ = function() { - if (this.timeoutId_ != null) { - clearTimeout(this.timeoutId_); - this.timeoutId_ = null; - } -}; - -/** - * Updates the training error UI. - * @private - */ -Flow.prototype.updateErrorUI_ = function() { - if (!this.training_) - return; - - var trainingSteps = this.getCurrentTrainingStep_('listening'); - var steps = trainingSteps.steps; - - $(this.trainingPagePrefix_ + '-toast').hidden = - this.trainingState_ != TrainingState.TIMEOUT; - if (this.trainingState_ == TrainingState.RESET) { - // We reset the training to begin at the first step. - // The first step is reset to 'listening', while the rest - // are reset to 'not-started'. - var prompt = loadTimeData.getString('trainingFirstPrompt'); - for (var i = 0; i < steps.length; ++i) { - steps[i].classList.remove('recorded'); - if (i == 0) { - steps[i].classList.remove('not-started'); - steps[i].classList.add('listening'); - } else { - steps[i].classList.add('not-started'); - if (i == steps.length - 1) - prompt = loadTimeData.getString('trainingLastPrompt'); - else - prompt = loadTimeData.getString('trainingMiddlePrompt'); - } - steps[i].querySelector('.text').textContent = prompt; - } - - // Reset the buttonbar. - $(this.trainingPagePrefix_ + '-processing').hidden = true; - $(this.trainingPagePrefix_ + '-wait').hidden = false; - $(this.trainingPagePrefix_ + '-error').hidden = true; - $(this.trainingPagePrefix_ + '-retry').hidden = true; - } else if (this.trainingState_ == TrainingState.TIMEOUT) { - var curStep = trainingSteps.current; - if (curStep) { - curStep.classList.remove('listening'); - curStep.classList.add('not-started'); - } - - // Set a timeout before focusing the Retry button so that screenreaders - // have time to announce the timeout first. - this.setTimeout_(function() { - $(this.trainingPagePrefix_ + '-toast').children[1].focus(); - }.bind(this), 50); - } else if (this.trainingState_ == TrainingState.ERROR) { - // Update the buttonbar. - $(this.trainingPagePrefix_ + '-wait').hidden = true; - $(this.trainingPagePrefix_ + '-error').hidden = false; - $(this.trainingPagePrefix_ + '-retry').hidden = false; - $(this.trainingPagePrefix_ + '-processing').hidden = false; - - // Set a timeout before focusing the Retry button so that screenreaders - // have time to announce the error first. - this.setTimeout_(function() { - $(this.trainingPagePrefix_ + '-retry').children[0].focus(); - }.bind(this), 50); - } -}; - -/** - * Handles a hotword trigger event and updates the training UI. - * @private - */ -Flow.prototype.handleHotwordTrigger_ = function() { - var trainingSteps = this.getCurrentTrainingStep_('listening'); - - if (!trainingSteps.current) - return; - - var index = trainingSteps.index; - this.hotwordTriggerReceived_[index] = true; - - trainingSteps.current.querySelector('.text').textContent = - loadTimeData.getString('trainingRecorded'); - trainingSteps.current.classList.remove('listening'); - trainingSteps.current.classList.add('recorded'); - - if (trainingSteps.steps[index + 1]) { - trainingSteps.steps[index + 1].classList.remove('not-started'); - trainingSteps.steps[index + 1].classList.add('listening'); - this.waitForHotwordTrigger_(index + 1); - return; - } - - // Only the last step makes it here. - var buttonElem = $(this.trainingPagePrefix_ + '-processing').hidden = false; - this.finalizeSpeakerModel_(); -}; - -/** - * Handles a chrome.idle.onStateChanged event and times out the training if - * the state is "locked". - * @param {!string} state State, one of "active", "idle", or "locked". - * @private - */ -Flow.prototype.handleIdleStateChanged_ = function(state) { - if (state == 'locked') - this.timeoutTraining_(); -}; - -/** - * Handles a chrome.hotwordPrivate.onEnabledChanged event and times out - * training if the user is no longer the active user (user switches profiles). - * @private - */ -Flow.prototype.handleEnabledChanged_ = function() { - if (chrome.hotwordPrivate.getStatus) { - chrome.hotwordPrivate.getStatus(function(status) { - if (status.userIsActive) - return; - - this.timeoutTraining_(); - }.bind(this)); - } -}; - -/** - * Gets and starts the appropriate flow for the launch mode. - * @param {chrome.hotwordPrivate.LaunchState} state Launch state of the - * Hotword Audio Verification App. - * @private - */ -Flow.prototype.startFlowForMode_ = function(state) { - this.launchMode_ = state.launchMode; - assert( - state.launchMode >= 0 && state.launchMode < FLOWS.length, - 'Invalid Launch Mode.'); - this.currentFlow_ = FLOWS[state.launchMode]; - if (state.launchMode == LaunchMode.HOTWORD_ONLY) { - $('intro-description-audio-history-enabled').hidden = false; - } else if (state.launchMode == LaunchMode.HOTWORD_AND_AUDIO_HISTORY) { - $('intro-description').hidden = false; - } - - this.advanceStep(); -}; - -/** - * Displays the current step. If the current step is not the first step, - * also hides the previous step. Focuses the current step's first button. - * @private - */ -Flow.prototype.showStep_ = function() { - var currentStepId = this.currentFlow_[this.currentStepIndex_]; - var currentStep = document.getElementById(currentStepId); - currentStep.hidden = false; - - cr.ui.setInitialFocus(currentStep); - - var previousStep = null; - if (this.currentStepIndex_ > 0) - previousStep = this.currentFlow_[this.currentStepIndex_ - 1]; - - if (previousStep) - document.getElementById(previousStep).hidden = true; - - chrome.app.window.current().show(); -}; - -window.Flow = Flow; -})(); diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/gradient-1x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/gradient-1x.png Binary files differdeleted file mode 100644 index 6ff742000e8..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/gradient-1x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/gradient-2x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/gradient-2x.png Binary files differdeleted file mode 100644 index 3c39792f23c..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/gradient-2x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-blue-1x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-blue-1x.png Binary files differdeleted file mode 100644 index c04541c591b..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-blue-1x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-blue-2x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-blue-2x.png Binary files differdeleted file mode 100644 index 7fca135f4b6..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-blue-2x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-gray-1x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-gray-1x.png Binary files differdeleted file mode 100644 index 1071613cb1b..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-gray-1x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-gray-2x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-gray-2x.png Binary files differdeleted file mode 100644 index e93d6e18eb9..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-check-gray-2x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-error-1x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-error-1x.png Binary files differdeleted file mode 100644 index f8d75841375..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-error-1x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-error-2x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-error-2x.png Binary files differdeleted file mode 100644 index dc0bf4f1c93..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-error-2x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-x-white-1x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-x-white-1x.png Binary files differdeleted file mode 100644 index 23d17c9c322..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-x-white-1x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-x-white-2x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-x-white-2x.png Binary files differdeleted file mode 100644 index 63cf9729d34..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/ic-x-white-2x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/icon-128.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/icon-128.png Binary files differdeleted file mode 100644 index d3a010c5376..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/icon-128.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/icon-16.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/icon-16.png Binary files differdeleted file mode 100644 index 3a7caeb0c2f..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/icon-16.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/icon-48.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/icon-48.png Binary files differdeleted file mode 100644 index 1f48e48098e..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/icon-48.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/intro-1x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/intro-1x.png Binary files differdeleted file mode 100644 index 3190980fbf9..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/intro-1x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/intro-2x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/intro-2x.png Binary files differdeleted file mode 100644 index 6465825b0ae..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/intro-2x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/placeholder-loader-1x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/placeholder-loader-1x.png Binary files differdeleted file mode 100644 index 0bb41bc244e..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/placeholder-loader-1x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/images/placeholder-loader-2x.png b/chromium/chrome/browser/resources/hotword_audio_verification/images/placeholder-loader-2x.png Binary files differdeleted file mode 100644 index cd1ffa7205b..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/images/placeholder-loader-2x.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/main.html b/chromium/chrome/browser/resources/hotword_audio_verification/main.html deleted file mode 100644 index 2572c9464c9..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/main.html +++ /dev/null @@ -1,25 +0,0 @@ -<!doctype html> -<html i18n-values="dir:textdirection;lang:language"> - <head> - <meta charset=utf-8> - <title i18n-content="introTitle"></title> - <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> - <link type="text/css" rel="stylesheet" href="style.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/node_utils.js"></script> - <script src="chrome://resources/js/load_time_data.js"></script> - <script src="chrome://resources/js/util.js"></script> - <script src="chrome://resources/js/i18n_template_no_process.js"></script> - <script src="flow.js"></script> - <script src="main.js"></script> - </head> - <body> - <div id="steps"> - <include src="steps/start_step.html"> - <include src="steps/audio_history_step.html"> - <include src="steps/speech_training_step.html"> - <include src="steps/finished_step.html"> - </div> - </body> -</html> diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/main.js b/chromium/chrome/browser/resources/hotword_audio_verification/main.js deleted file mode 100644 index 33fc21a9ec4..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/main.js +++ /dev/null @@ -1,50 +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. - -var appWindow = chrome.app.window.current(); - -document.addEventListener('DOMContentLoaded', function() { - chrome.hotwordPrivate.getLocalizedStrings(function(strings) { - loadTimeData.data = strings; - i18nTemplate.process(document, loadTimeData); - - var flow = new Flow(); - flow.startFlow(); - - var pressFunction = function(e) { - // Only respond to 'Enter' key presses. - if (e.type == 'keyup' && e.key != 'Enter') - return; - - var classes = e.target.classList; - if (classes.contains('close') || classes.contains('finish-button')) { - flow.stopTraining(); - appWindow.close(); - e.preventDefault(); - } - if (classes.contains('retry-button')) { - flow.handleRetry(); - e.preventDefault(); - } - }; - - $('steps').addEventListener('click', pressFunction); - $('steps').addEventListener('keyup', pressFunction); - - $('audio-history-agree').addEventListener('click', function(e) { - flow.enableAudioHistory(); - e.preventDefault(); - }); - - $('hotword-start').addEventListener('click', function(e) { - flow.advanceStep(); - e.preventDefault(); - }); - - $('settings-link').addEventListener('click', function(e) { - chrome.browser.openTab({'url': 'chrome://settings'}, function() {}); - e.preventDefault(); - }); - }); -}); diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/manifest.json b/chromium/chrome/browser/resources/hotword_audio_verification/manifest.json deleted file mode 100644 index a6e4091e39c..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/manifest.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - // chrome-extension://abjokfonkihficiokmkfboogholifghn/ - "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtfVZc80kw93gaZwHGhOLpxGKil8n/x1IQ2L/Oj76YeICJk5w4YKw+0N2IYA0gJnwqJdTu3ijrWqO2oQTWoZ3lHmAv9NHsUXHdrWuoGRuVqo0dakLnS+AB3rLGPSerZZNExpdK0Yd3wR6GMOJAoAQM9H6Zpo7LNYjPebx31QJZ6OgdYZA+Eu4fGIJkWIPY1LMsVO1jzFJ4JSPyNWmhxL4fHfQXVM5p1cgJSVxTXsB1ZGaRc4HF2kwSYMOimIiWqfU0VInTXVU7IS3hJaKzm/LExW/ABTGejf2sGIa725EQTavGFsQ07jFZdVzKGAjHCU/0Jy8PxDIg2B+ixlM2QXP/wIDAQAB", - "name": "Ok Google", - "version": "0.1", - "manifest_version": 2, - "icons": { - "16": "images/icon-16.png", - "48": "images/icon-48.png", - "128": "images/icon-128.png" - }, - "app": { - "background": { - "scripts": ["event_page.js"], - "persistent": false - }, - "content_security_policy": "default-src 'self' blob: filesystem:; script-src 'self' blob: filesystem: chrome://resources chrome://settings; style-src 'self' blob: filesystem: chrome://resources" - }, - "permissions": [ - "chrome://resources/", - "chrome://settings/", - "browser", - "hotwordPrivate", - "idle" - ], - "display_in_launcher": false -} diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/steps/audio_history_step.html b/chromium/chrome/browser/resources/hotword_audio_verification/steps/audio_history_step.html deleted file mode 100644 index 8c2304ba9ab..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/steps/audio_history_step.html +++ /dev/null @@ -1,38 +0,0 @@ -<div id="audio-history-container" hidden> - <div class="container"> - <span class="close" tabindex="0" role="button" - i18n-values="aria-label:close"></span> - <div class="header"> - <h1 i18n-content="audioHistoryTitle" aria-live="polite"></h1> - </div> - <div class="content"> - <div i18n-content="audioHistoryDescription1"></div> - <div class="v-spacing"></div> - <div i18n-values=".innerHTML:audioHistoryDescription2"></div> - <div class="v-spacing"></div> - <div i18n-values=".innerHTML:audioHistoryDescription3"></div> - </div> - <div class="buttonbar"> - <div class="right"> - <div> - <button id="audio-history-agree" i18n-content="audioHistoryAgree" - class="primary"> - </button> - <button id="audio-history-cancel" i18n-content="cancel" - class="finish-button"> - </button> - </div> - </div> - <div class="left"> - <div id="audio-history-wait" class="message wait" hidden> - <span class="icon"></span> - <span i18n-content="audioHistoryWait" class="text"></span> - </div> - <div id="audio-history-error" class="message error" role="alert" hidden> - <span class="icon"></span> - <span i18n-content="error" class="text"></span> - </div> - </div> - </div> - </div> -</div> diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/steps/finished_step.html b/chromium/chrome/browser/resources/hotword_audio_verification/steps/finished_step.html deleted file mode 100644 index 71537dd45aa..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/steps/finished_step.html +++ /dev/null @@ -1,30 +0,0 @@ -<div id="finish-container" hidden> - <div class="container"> - <span class="close" tabindex="0" role="button" - i18n-values="aria-label:close"></span> - <div class="header"> - <h1 i18n-content="finishedTitle" aria-live="polite"></h1> - </div> - <div class="content"> - <h3 i18n-content="finishedListIntro"></h3> - <div class="check"> - <span class="icon"></span> - <span i18n-content="finishedListItem1" class="text"> - </span> - </div> - <div class="check"> - <span class="icon"></span> - <span i18n-content="finishedListItem2" class="text"> - </span> - </div> - <div i18n-values=".innerHTML:finishedSettings"></div> - <div i18n-values=".innerHTML:finishedAudioHistory"></div> - </div> - <div class="buttonbar"> - <div class="right"> - <button id="done-button" i18n-content="finish" - class="primary finish-button"></button> - </div> - </div> - </div> -</div> diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/steps/speech_training_step.html b/chromium/chrome/browser/resources/hotword_audio_verification/steps/speech_training_step.html deleted file mode 100644 index fd37331147a..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/steps/speech_training_step.html +++ /dev/null @@ -1,56 +0,0 @@ -<div id="speech-training-container" hidden> - <div class="container"> - <span class="close" tabindex="0" role="button" - i18n-values="aria-label:close"></span> - <div class="header"> - <h1 i18n-content="trainingTitle" aria-live="polite"></h1> - </div> - <div id="speech-training-training" class="content"> - <div class="col-2"> - <div i18n-content="trainingDescription"></div> - <br> - <h3 i18n-content="trainingSpeak"></h3> - </div> - <div class="col-spacing"></div> - <div class="col-2"> - <div class="train listening"> - <span class="icon"></span> - <span i18n-content="trainingFirstPrompt" - class="text"></span> - </div> - <div class="train not-started"> - <span class="icon"></span> - <span i18n-content="trainingMiddlePrompt" - class="text"></span> - </div> - <div class="train not-started"> - <span class="icon"></span> - <span i18n-content="trainingLastPrompt" - class="text"></span> - </div> - </div> - </div> - <div id="speech-training-toast" class="toast" hidden> - <span i18n-content="trainingTimeout" class="message" role="alert"></span> - <button i18n-content="trainingRetry" class="retry-button" tabindex="0"> - </button> - </div> - <div id="speech-training-processing" class="buttonbar" hidden> - <div id="speech-training-retry" class="right" hidden> - <button i18n-content="trainingRetry" class="primary retry-button"> - </button> - </div> - <div class="left"> - <div id="speech-training-wait" class="message wait"> - <span class="icon"></span> - <span i18n-content="finishedWait" class="text"></span> - </div> - <div id="speech-training-error" class="message error" - role="alert" hidden> - <span class="icon"></span> - <span i18n-content="error" class="text"></span> - </div> - </div> - </div> - </div> -</div> diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/steps/start_step.html b/chromium/chrome/browser/resources/hotword_audio_verification/steps/start_step.html deleted file mode 100644 index 441ae6b86aa..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/steps/start_step.html +++ /dev/null @@ -1,17 +0,0 @@ -<div id="start-container" hidden> - <div class="container"> - <span class="close" tabindex="0" role="button" - i18n-values="aria-label:close"></span> - <div class="intro-image"></div> - <div class="intro-text"> - <h1 i18n-content="introTitle"></h1> - <h2 i18n-content="introSubtitle"></h2> - <h3 id="intro-description" i18n-content="introDescription" hidden></h3> - <h3 id="intro-description-audio-history-enabled" - i18n-content="introDescriptionAudioHistoryEnabled" hidden></h3> - </div> - <div class="buttonbar"> - <button id="hotword-start" i18n-content="introStart"></button> - </div> - </div> -</div> diff --git a/chromium/chrome/browser/resources/hotword_audio_verification/style.css b/chromium/chrome/browser/resources/hotword_audio_verification/style.css deleted file mode 100644 index 233ad3143f7..00000000000 --- a/chromium/chrome/browser/resources/hotword_audio_verification/style.css +++ /dev/null @@ -1,361 +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(xdai): Remove hard-coded font-family for 'Roboto'. */ - -* { - box-sizing: border-box; - color: rgba(0, 0, 0, .54); - font-family: Roboto, 'Noto Sans', sans-serif; - font-size: 13px; - margin: 0; - padding: 0; -} - -#start-container * { - color: #fff; -} - -#start-container h2 { - font-size: 15px; - font-weight: normal; - line-height: 24px; - margin-top: 16px; -} - -#start-container h3 { - font-weight: normal; - margin: 42px 16px 24px 16px; -} - -#start-container div.container { - background: rgb(66, 133, 244); -} - -div.intro-image { - background: -webkit-image-set( - url(../images/intro-1x.png) 1x, - url(../images/intro-2x.png) 2x) - no-repeat; - height: 152px; - left: 24px; - position: absolute; - top: 122px; - width: 304px; -} - -div.intro-text { - left: 328px; - position: absolute; - text-align: center; - top: 116px; - width: 432px; -} - -#start-container div.buttonbar { - background-color: rgb(51, 103, 214); - height: 56px; - padding: 0; - text-align: center; -} - -#start-container .buttonbar button { - height: 100%; - margin: 0; - padding: 0 8px; - width: 100%; -} - -a { - -webkit-app-region: no-drag; - color: rgb(51, 103, 214); - text-decoration: none; -} - -button { - -webkit-app-region: no-drag; -} - -body { - -webkit-app-region: drag; - background: #ddd; -} - -h1 { - font-size: 20px; - font-weight: normal; - line-height: 32px; -} - -h3 { - font-size: 13px; - line-height: 20px; -} - -div.container { - background: #fff; - height: 448px; - position: relative; - width: 784px; -} - -div.header { - background: -webkit-image-set( - url(../images/gradient-1x.png) 1x, - url(../images/gradient-2x.png) 2x) - no-repeat; - height: 128px; - padding: 70px 42px 0 42px; -} - -div.header h1 { - color: #fff; -} - -div.content { - height: 264px; - line-height: 20px; - padding: 32px 42px 0 42px; -} - -div.content h3 { - color: rgba(0, 0, 0, .87); - margin-bottom: 16px; -} - -div.col-2 { - color: rgba(0, 0, 0, .54); - float: left; - width: 320px; -} - -div.col-spacing { - float: left; - height: 216px; - width: 60px; -} - -div.v-spacing { - height: 8px; -} - -a[is='action-link'] { - display: inline-block; - font-size: 14px; - margin-top: 22px; - text-decoration: none; - text-transform: uppercase; -} - -.train { - clear: both; - line-height: 18px; - margin-bottom: 24px; -} - -.train .icon { - display: inline-block; - height: 18px; - margin-right: 8px; - vertical-align: top; - width: 18px; -} - -.train .text { - color: rgba(0, 0, 0, .54); - display: inline-block; - line-height: 13px; - padding-top: 3px; - vertical-align: top; -} - -.train.recorded .text { - color: rgba(66, 133, 244, 1); -} - -@keyframes rotate { - from { transform: rotate(0); } - to { transform: rotate(359deg); } -} - -.train.listening .icon { - animation: rotate 2s linear infinite; - background: -webkit-image-set( - url(../images/placeholder-loader-1x.png) 1x, - url(../images/placeholder-loader-2x.png) 2x) - no-repeat; -} - -.train.not-started .icon { - background: -webkit-image-set( - url(../images/ic-check-gray-1x.png) 1x, - url(../images/ic-check-gray-2x.png) 2x) - no-repeat; -} - -.train.recorded .icon { - background: -webkit-image-set( - url(../images/ic-check-blue-1x.png) 1x, - url(../images/ic-check-blue-2x.png) 2x) - no-repeat; -} - -.check { - clear: both; - height: 18px; - margin-bottom: 24px; -} - -.check .icon { - background: -webkit-image-set( - url(../images/ic-check-blue-1x.png) 1x, - url(../images/ic-check-blue-2x.png) 2x) - no-repeat; - display: inline-block; - height: 18px; - margin-right: 8px; - vertical-align: top; - width: 18px; -} - -.check .text { - color: rgba(0, 0, 0, .54); - display: inline-block; - height: 18px; - line-height: 18px; - padding-top: 2px; - vertical-align: top; -} - -div.buttonbar { - background-color: rgba(236,239, 241, 1); - bottom: 0; - height: 56px; - padding: 12px; - position: absolute; - width: 100%; -} - -.buttonbar button { - background: none; - border: none; - display: inline-block; - font-weight: 700; - height: 32px; - line-height: 32px; - margin-left: 8px; - min-width: 56px; - padding: 1px 8px 0 8px; - text-transform: uppercase; -} - -.buttonbar button:disabled { - opacity: .5; -} - -.buttonbar button.grayed-out { - color: rgba(0, 0, 0, .28); - text-transform: none; -} - -.buttonbar button.primary { - color: rgb(51, 103, 214); -} - -.buttonbar .left { - float: left; - text-align: left; -} - -.buttonbar .left button:first-child { - margin-left: 0; -} - -.buttonbar .right { - float: right; - text-align: right; -} - -.buttonbar .message { - margin: 7px 0 0 2px; -} - -.buttonbar .message .icon { - display: inline-block; - height: 18px; - margin-right: 8px; - vertical-align: top; - width: 18px; -} - -.buttonbar .message.wait .icon { - animation: rotate 2s linear infinite; - background: -webkit-image-set( - url(../images/placeholder-loader-1x.png) 1x, - url(../images/placeholder-loader-2x.png) 2x) - no-repeat; -} - -.buttonbar .message.error .icon { - background: -webkit-image-set( - url(../images/ic-error-1x.png) 1x, - url(../images/ic-error-2x.png) 2x) - no-repeat; -} - -.buttonbar .message .text { - color: rgba(0, 0, 0, .54); - display: inline-block; - line-height: 18px; - padding-top: 2px; - vertical-align: top; -} - -.buttonbar .message.error .text { - color: rgb(213, 0, 0); -} - -.close { - -webkit-app-region: no-drag; - background: -webkit-image-set( - url(../images/ic-x-white-1x.png) 1x, - url(../images/ic-x-white-2x.png) 2x) - center center no-repeat; - border: none; - float: right; - height: 42px; - opacity: .54; - width: 42px; -} - -.close:hover { - opacity: 1; -} - -.toast { - background-color: rgb(38, 50, 56); - bottom: 0; - height: 52px; - padding: 10px 12px 0 42px; - position: absolute; - width: 100%; -} - -.toast .message { - color: #fff; - float: left; - padding: 9px 0 0 0; -} - -.toast button { - background: none; - border: none; - color: rgb(58, 218, 255); - float: right; - height: 32px; - margin-left: 18px; - min-width: 56px; - padding: 0 8px 0 8px; - text-transform: uppercase; -} diff --git a/chromium/chrome/browser/resources/inspect/inspect.css b/chromium/chrome/browser/resources/inspect/inspect.css index 13875de17a2..5bd5a239edb 100644 --- a/chromium/chrome/browser/resources/inspect/inspect.css +++ b/chromium/chrome/browser/resources/inspect/inspect.css @@ -12,6 +12,8 @@ html { body { color: rgb(48, 57, 66); + display: flex; + flex-direction: column; font-size: 13px; height: 100%; margin: 0; @@ -38,18 +40,13 @@ img { #infobar { background: rgb(255, 212, 0); - height: 20px; - left: 0; - line-height: 20px; - position: fixed; - right: 0; + display: none; + padding: 4px 0; text-align: center; - visibility: hidden; - z-index: 1; } #infobar.show { - visibility: visible; + display: block; } #navigation { diff --git a/chromium/chrome/browser/resources/instant/instant.css b/chromium/chrome/browser/resources/instant/instant.css deleted file mode 100644 index 795df5d309d..00000000000 --- a/chromium/chrome/browser/resources/instant/instant.css +++ /dev/null @@ -1,15 +0,0 @@ -/* Copyright 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 { - font-size: 12px; - margin: 10px; - min-width: 47em; - padding-bottom: 65px; -} - -.row { - border-bottom: 1px solid #a0a0a0; - padding: 5px; -} diff --git a/chromium/chrome/browser/resources/instant/instant.html b/chromium/chrome/browser/resources/instant/instant.html deleted file mode 100644 index 80fdc497c14..00000000000 --- a/chromium/chrome/browser/resources/instant/instant.html +++ /dev/null @@ -1,24 +0,0 @@ -<!doctype html> -<html> -<!-- -Copyright 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. ---> -<head> -<meta charset="utf-8"> -<title>Instant preferences</title> -<link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> -<link rel="stylesheet" href="instant.css"> -<script src="instant.js"></script> -</head> -<body> - <h2>Instant preferences</h2> - <hr> - <div id="instant-form"></div> - <div class="buttons-pane"> - <button id="save-button">Save</button> - </div> - <hr/> -</body> -</html> diff --git a/chromium/chrome/browser/resources/instant/instant.js b/chromium/chrome/browser/resources/instant/instant.js deleted file mode 100644 index be590061c23..00000000000 --- a/chromium/chrome/browser/resources/instant/instant.js +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 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. - -// 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. -var $ = function(id) { - // eslint-disable-next-line no-restricted-properties - return document.getElementById(id); -}; - -/** - * WebUI for configuring instant.* preference values used by - * Chrome's instant search system. - */ -var instantConfig = (function() { - 'use strict'; - - /** List of fields used to dynamically build form. **/ - var FIELDS = [ - { - key: 'instant_ui.zero_suggest_url_prefix', - label: 'Prefix URL for the experimental Instant ZeroSuggest provider', - type: 'string', - size: 40, - units: '', - default: '' - }, - ]; - - /** - * Returns a DOM element of the given type and class name. - */ - function createElementWithClass(elementType, className) { - var element = document.createElement(elementType); - element.className = className; - return element; - } - - /** - * Dynamically builds web-form based on FIELDS list. - * @return {string} The form's HTML. - */ - function buildForm() { - var buf = []; - - for (var i = 0; i < FIELDS.length; i++) { - var field = FIELDS[i]; - - var row = createElementWithClass('div', 'row'); - row.id = ''; - - var label = createElementWithClass('label', 'row-label'); - label.setAttribute('for', field.key); - label.textContent = field.label; - row.appendChild(label); - - var input = createElementWithClass('input', 'row-input'); - input.type = field.type; - input.id = field.key; - input.title = 'Default Value: ' + field.default; - if (field.size) - input.size = field.size; - input.min = field.min || 0; - if (field.max) - input.max = field.max; - if (field.step) - input.step = field.step; - row.appendChild(input); - - var units = createElementWithClass('div', 'row-units'); - if (field.units) - units.innerHTML = field.units; - row.appendChild(units); - - $('instant-form').appendChild(row); - } - } - - /** - * Initialize the form by adding 'onChange' listeners to all fields. - */ - function initForm() { - for (var i = 0; i < FIELDS.length; i++) { - var field = FIELDS[i]; - $(field.key).onchange = (function(key) { - setPreferenceValue(key); - }).bind(null, field.key); - } - } - - /** - * Request a preference setting's value. - * This method is asynchronous; the result is provided by a call to - * getPreferenceValueResult. - * @param {string} prefName The name of the preference value being requested. - */ - function getPreferenceValue(prefName) { - chrome.send('getPreferenceValue', [prefName]); - } - - /** - * Handle callback from call to getPreferenceValue. - * @param {string} prefName The name of the requested preference value. - * @param {value} value The current value associated with prefName. - */ - function getPreferenceValueResult(prefName, value) { - if ($(prefName).type == 'checkbox') - $(prefName).checked = value; - else - $(prefName).value = value; - } - - /** - * Set a preference setting's value stored in the element with prefName. - * @param {string} prefName The name of the preference value being set. - */ - function setPreferenceValue(prefName) { - var value; - if ($(prefName).type == 'checkbox') - value = $(prefName).checked; - else if ($(prefName).type == 'number') - value = parseFloat($(prefName).value); - else - value = $(prefName).value; - chrome.send('setPreferenceValue', [prefName, value]); - } - - /** - * Saves data back into Chrome preferences. - */ - function onSave() { - for (var i = 0; i < FIELDS.length; i++) { - var field = FIELDS[i]; - setPreferenceValue(field.key); - } - return false; - } - - function loadForm() { - for (var i = 0; i < FIELDS.length; i++) - getPreferenceValue(FIELDS[i].key); - } - - /** - * Build and initialize the configuration form. - */ - function initialize() { - buildForm(); - loadForm(); - initForm(); - - $('save-button').onclick = onSave.bind(this); - } - - return { - initialize: initialize, - getPreferenceValueResult: getPreferenceValueResult - }; -})(); - -document.addEventListener('DOMContentLoaded', instantConfig.initialize); diff --git a/chromium/chrome/browser/resources/interventions_internals/OWNERS b/chromium/chrome/browser/resources/interventions_internals/OWNERS new file mode 100644 index 00000000000..0957d79411d --- /dev/null +++ b/chromium/chrome/browser/resources/interventions_internals/OWNERS @@ -0,0 +1,3 @@ +file://components/previews/OWNERS + +# COMPONENT: UI>Browser>Previews diff --git a/chromium/chrome/browser/resources/interventions_internals/index.css b/chromium/chrome/browser/resources/interventions_internals/index.css index 4ee2351451c..bb04a5b0888 100644 --- a/chromium/chrome/browser/resources/interventions_internals/index.css +++ b/chromium/chrome/browser/resources/interventions_internals/index.css @@ -4,6 +4,62 @@ * found in the LICENSE file. */ +body { + font-family: 'DejaVu Sans', Arial, sans-serif; + font-size: 75%; + user-select: none; +} + +.section-header { + color: rgb(43, 96, 222); + font-size: 120%; + font-weight: bold; + margin-top: 20px; +} + +.previews-status-value { + margin-top: 5px; +} + +.previews-flag-container { + margin-top: 5px; +} + +.previews-flag-value { + margin-left: 10px; +} + +button { + background: rgb(53, 106, 222); + border-radius: 5px; + color: white; + font-size: 65%; + font-weight: bold; + margin: 5px 0; + padding: 5px; + text-align: center; + text-decoration: none; + text-transform: uppercase; +} + +.copy-to-clipboard-button { + background: rgb(53, 106, 222); + border-radius: 2px; + color: white; + float: right; + font-size: 60%; + font-weight: bold; + margin: 2px; + padding: 2px; + text-align: center; + text-decoration: none; + text-transform: uppercase; +} + +#clear-log-button { + float: right; +} + .hidden-tab { display: none; padding: 12px; @@ -14,40 +70,197 @@ padding: 12px; } +.tab-select { + left: 0; + right: 0; +} + +nav { + border-bottom: 1px solid silver; +} + .tab-select input[type=radio] { - display: none; + display: none; } .tab-select label { - background: #ddd; - border: outset 1px silver; + border: 2px solid white; + border-bottom: 0; + border-top-left-radius: 8px; + border-top-right-radius: 8px; cursor: pointer; - margin-right: -5px; - padding: 5px 5px; + display: table-cell; + padding: 5px; + position: relative; + text-align: center; + vertical-align: middle; + width: 25%; +} + +.inactive-tab { + background: #ddd; + color: black; } -.tab-select input:checked + span { - background: #aaa; - border: inset 1px silver; +.active-tab { + background: rgb(0, 128, 255); + color: white; +} + +.table-name { + color: rgb(43, 96, 222); + font-size: 100%; + font-weight: bold; + margin-bottom: 5px; + margin-top: 5px; } table { - border: 1px solid black; - border-collapse: collapse; + border-collapse: separate; + font-size: 90%; + margin-top: 10px; + max-height: 100px; + table-layout: fixed; + width: 100%; } -th { - border: 1px solid black; - padding: 15px; - text-align: left; +.nqe-value-column { + text-align: center; } td { - border: 1px solid black; - padding: 15px; + border: 1px solid white; + padding: 10px; text-align: left; } +tr:nth-child(odd) td { + background: rgb(240, 255, 255); +} + +th { + background: rgb(0, 191, 255); + border: 0; + color: white; + padding-bottom: 5px; + padding-top: 5px; + text-align: center; +} + +td.log-time { + font-size: 80%; +} + +td.log-description { + font-size: 80%; +} + +td.log-url { + border-bottom: 1px transparent; + font-size: 80%; + position: relative; + word-wrap: break-word; +} + +.log-type { + text-align: center; + width: 20%; +} + +td.log-type { + font-size: 80%; +} + +.url-tooltip { + background-color: black; + border-radius: 6px; + color: #fff; + left: 0; + padding: 5px; + position: absolute; + top: 100%; + visibility: hidden; + width: 100%; + z-index: 1; +} + +td.log-url:hover .url-tooltip { + opacity: 1; + visibility: visible; +} + +td.log-url .url-tooltip::after { + border-color: transparent transparent black transparent; + border-style: solid; + border-width: 5px; + bottom: 100%; + content: ' '; + left: 50%; + margin-left: -5px; + position: absolute; +} + +@media(min-device-width: 600px) { + body { + font-size: 85%; + } + + #nqe-logs-table { + width: 60%; + } + + #blacklisted-hosts-table { + width: 60%; + } + + #previews-flags-table { + width: 30%; + } + + .log-time { + width: 15%; + } + + td.log-time { + font-size: 80%; + } + + .log-type { + width: 10%; + } + + td.log-type { + font-siz15e: 100%; + } +} + +@media(min-device-width: 1024px) { + body { + font-size: 100%; + } + + .copy-to-clipboard-button { + font-size: 80%; + } + + .log-time { + font-size: 100%; + width: 8%; + } + + #nqe-logs-table { + width: 30%; + } + + #previews-flags-table { + width: 30%; + } + + td.log-description { + font-size: 100%; + } +} + .error-header { font-size: 150%; font-weight: bold; @@ -56,3 +269,9 @@ td { .error-message { font-style: italic; } + +#blacklist-ignored-status { + color: red; + font-style: italic; + font-weight: bold; +} diff --git a/chromium/chrome/browser/resources/interventions_internals/index.html b/chromium/chrome/browser/resources/interventions_internals/index.html index 4de45b78f77..da429f25a78 100644 --- a/chromium/chrome/browser/resources/interventions_internals/index.html +++ b/chromium/chrome/browser/resources/interventions_internals/index.html @@ -1,8 +1,8 @@ <!DOCTYPE html> -<html> +<html lang="en"> <head> <meta charset="utf-8"> - <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Interventions-Internals</title> <script src="chrome://resources/js/cr.js"></script> <script src="chrome://resources/js/mojo_bindings.js"></script> @@ -15,29 +15,101 @@ <body> <nav class="tab-select"> - <label> - <input type="radio" id="statuses" name="tabs" - value="previews-statuses" checked="checked"> - <span>Previews Modes</span> + <label class="active-tab"> + <input type="radio" id="statuses-tab" name="tabs" + value="previews-status" checked="checked"> + <span>Interventions</span> </label> - <label> - <input type="radio" id="logs" name="tabs" value="message-logs"> + <label class="inactive-tab"> + <input type="radio" id="logs-tab" name="tabs" value="message-logs"> <span>Logs</span> </label> + <label class="inactive-tab"> + <input type="radio" id="blacklist-tab" name="tabs" + value="blacklist-status"> + <span>Blacklist Status</span> + </label> + <label class="inactive-tab"> + <input type="radio" id="nqe-tab" name="tabs" value="nqe-info"> + <span>Network Quality</span> + </label> </nav> - <div class="tab-content" id="previews-statuses"> + <div class="tab-content" id="previews-status"> + <div id="previews-enabled-status"> + <div class="section-header"> + Intervention Status + </div> + </div> + <div id="previews-flags-status"> + <div class="section-header"> + Intervention Flags + </div> + <table id="previews-flags-table"> + <tr> + <th class="flag-name" id="flag-name-header">Flag</th> + <th class="flag-value" id="flag-value-header">Value</th> + </tr> + </table> + </div> </div> <div class="tab-content" id="message-logs"> + <div class="table-name">Interventions Logs</div> + <input type="text" id="log-search-bar" + placeholder="Search for something ..."> + <button id="clear-log-button" type="button">Clear logs</button> <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> + <th class="log-time" id="time-table-header">Time</th> + <th class="log-type" id="type-table-header">Type</th> + <th class="log-description" id="description-table-header"> + Description + </th> + <th class="log-url" id="url-table-header">URL</th> </tr> </table> </div> + + <div class="tab-content" id="blacklist-status"> + <div id="blacklist-ignored-status"></div> + <button id="ignore-blacklist-button" type="button"> + Ignore Blacklist + </button> + <div id="user-blacklisted-status"> + User blacklisted status: + <span id="user-blacklisted-status-value">N/A</span> + </div> + <div id="blacklist-cleared-status"> + Last blacklist cleared: + <span id="blacklist-last-cleared-time">N/A</span> + </div> + + <div id="blacklisted-hosts"> + <div class="table-name">Blacklisted hosts</div> + <table id="blacklisted-hosts-table"> + <tr> + <th id="blacklisted-host-header">Host</th> + <th id="blacklisted-time-header">Time</th> + </tr> + </table> + </div> + </div> + + <div class="tab-content" id="nqe-info"> + <div id="nqe-status"> + Estimated effective connection type: + <span id="nqe-type">N/A</span> + </div> + <div id="nqe-logs"> + <div class="table-name">Network Quality Change Log</div> + <table id="nqe-logs-table"> + <tr> + <th id="nqe-time-header">Time</th> + <th id="nqe-value-header">Connection</th> + </tr> + </table> + </div> + </div> </body> </html> diff --git a/chromium/chrome/browser/resources/interventions_internals/index.js b/chromium/chrome/browser/resources/interventions_internals/index.js index b01f1b2bfe8..39304a937fe 100644 --- a/chromium/chrome/browser/resources/interventions_internals/index.js +++ b/chromium/chrome/browser/resources/interventions_internals/index.js @@ -2,15 +2,78 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** The columns that are used to find rows that contain the keyword. */ +const KEY_COLUMNS = ['log-type', 'log-description', 'log-url']; +const ENABLE_BLACKLIST_BUTTON = 'Enable Blacklist'; +const IGNORE_BLACKLIST_BUTTON = 'Ignore Blacklist'; +const IGNORE_BLACKLIST_MESSAGE = 'Blacklist decisions are ignored.'; +const URL_THRESHOLD = 40; // Maximum URL length + +/** + * Convert milliseconds to human readable date/time format. + * The return format will be "MM/dd/YYYY hh:mm:ss.sss" + * @param {number} time Time in millisecond since Unix Epoch. + * @return The converted string format. + */ +function getTimeFormat(time) { + let date = new Date(time); + let options = { + year: 'numeric', + month: '2-digit', + day: '2-digit', + }; + + let dateString = date.toLocaleDateString('en-US', options); + return dateString + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + + date.getSeconds() + '.' + date.getMilliseconds(); +} + +/** + * Insert a log message row to the top of the log message table. + * + * @param {number!} time Millisecond since Unix Epoch representation of time. + * @param {string!} type The message event type. + * @param {string!} description The event message description. + * @param {string} url The URL associated with the event. + */ +function insertMessageRowToMessageLogTable(time, type, description, url) { + let tableRow = + $('message-logs-table').insertRow(1); // Index 0 belongs to header row. + tableRow.setAttribute('class', 'log-message'); + + let timeTd = document.createElement('td'); + timeTd.textContent = getTimeFormat(time); + timeTd.setAttribute('class', 'log-time'); + tableRow.appendChild(timeTd); + + let typeTd = document.createElement('td'); + typeTd.setAttribute('class', 'log-type'); + typeTd.textContent = type; + tableRow.appendChild(typeTd); + + let descriptionTd = document.createElement('td'); + descriptionTd.setAttribute('class', 'log-description'); + descriptionTd.textContent = description; + tableRow.appendChild(descriptionTd); + + if (url.length > 0) { + let urlTd = createUrlElement(url); + urlTd.setAttribute('class', 'log-url'); + tableRow.appendChild(urlTd); + } +} + /** * 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); + let selected = document.querySelector('input[type=radio][name=tabs]:checked'); + let selectedTab = document.querySelector('#' + selected.value); + selectedTab.className = selectedTab.className.replace('hidden-tab', 'selected-tab'); + selected.parentElement.className = + selected.parentElement.className.replace('inactive-tab', 'active-tab'); } /** @@ -19,8 +82,10 @@ function setSelectedTab() { */ function changeTab() { let lastSelected = document.querySelector('.selected-tab'); + let lastTab = document.querySelector('.active-tab'); lastSelected.className = lastSelected.className.replace('selected-tab', 'hidden-tab'); + lastTab.className = lastTab.className.replace('active-tab', 'inactive-tab'); setSelectedTab(); } @@ -44,6 +109,110 @@ function setupTabControl() { setSelectedTab(); } +/** + * Initialize the search functionality of the search bar on the log tab. + * Searching will hide any rows that don't contain the keyword in the search + * bar. + */ +function setupLogSearch() { + $('log-search-bar').addEventListener('keyup', () => { + let keyword = $('log-search-bar').value.toUpperCase(); + let rows = document.querySelectorAll('.log-message'); + + rows.forEach((row) => { + let found = KEY_COLUMNS.some((column) => { + return (row.querySelector('.' + column) + .textContent.toUpperCase() + .includes(keyword)); + }); + row.style.display = found ? '' : 'none'; + }); + }); +} + +/** + * Create and add a copy to clipboard button to a given node. + * + * @param {string} text The text that will be copied to the clipboard. + * @param {element!} node The node that will have the button appended to. + */ +function appendCopyToClipBoardButton(text, node) { + if (!document.queryCommandSupported || + !document.queryCommandSupported('copy')) { + // Don't add copy to clipboard button if not supported. + return; + } + let copyButton = document.createElement('div'); + copyButton.setAttribute('class', 'copy-to-clipboard-button'); + copyButton.textContent = 'Copy'; + + copyButton.addEventListener('click', () => { + var textarea = document.createElement('textarea'); + textarea.textContent = text; + document.body.appendChild(textarea); + textarea.select(); + try { + return document.execCommand('copy'); // Security exception may be thrown. + } catch (ex) { + console.warn('Copy to clipboard failed.', ex); + return false; + } finally { + document.body.removeChild(textarea); + } + }); + node.appendChild(copyButton); +} + +/** + * Shorten long URL string so that it can be displayed nicely on mobile devices. + * If |url| is longer than URL_THRESHOLD, then it will be shorten, and a tooltip + * element will be added so that user can see the original URL. + * + * Add copy to clipboard button to it. + * + * @param {string} url The given URL string. + * @return An DOM node with the original URL if the length is within THRESHOLD, + * or the shorten URL with a tooltip element at the end of the string. + */ +function createUrlElement(url) { + let urlCell = document.createElement('div'); + urlCell.setAttribute('class', 'log-url-value'); + let urlTd = document.createElement('td'); + urlTd.appendChild(urlCell); + + if (url.length <= URL_THRESHOLD) { + urlCell.textContent = url; + } else { + urlCell.textContent = url.substring(0, URL_THRESHOLD - 3) + '...'; + let tooltip = document.createElement('span'); + tooltip.setAttribute('class', 'url-tooltip'); + tooltip.textContent = url; + urlTd.appendChild(tooltip); + } + + // Append copy to clipboard button. + appendCopyToClipBoardButton(url, urlTd); + return urlTd; +} + +/** + * Helper function to remove all log message from log-messages-table. + */ +function removeAllLogMessagesRows() { + let logsTable = $('message-logs-table'); + for (let row = logsTable.rows.length - 1; row > 0; row--) { + logsTable.deleteRow(row); + } +} + +/** + * Initialize the button to clear out all the log messages. This button only + * remove the logs from the UI, and does not effect any decision made. + */ +function setupLogClear() { + $('clear-log-button').addEventListener('click', removeAllLogMessagesRows); +} + /** @constructor */ let InterventionsInternalPageImpl = function(request) { this.binding_ = @@ -59,34 +228,118 @@ InterventionsInternalPageImpl.prototype = { * PreviewsLogger. */ logNewMessage: function(log) { - let logsTable = $('message-logs-table'); - let tableRow = document.createElement('tr'); - tableRow.setAttribute('class', 'log-message'); + insertMessageRowToMessageLogTable( + log.time, log.type, log.description, log.url.url); + }, + + /** + * Update new blacklisted host to the web page. + * + * @override + * @param {!string} host The blacklisted host. + * @param {number} time The time when the host was blacklisted in milliseconds + * since Unix epoch. + */ + onBlacklistedHost: function(host, time) { + let row = document.createElement('tr'); + row.setAttribute('class', 'blacklisted-host-row'); + + let hostTd = document.createElement('td'); + hostTd.setAttribute('class', 'host-blacklisted'); + hostTd.textContent = host; + row.appendChild(hostTd); 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); + timeTd.setAttribute('class', 'host-blacklisted-time'); + timeTd.textContent = getTimeFormat(time); + row.appendChild(timeTd); + + // TODO(thanhdle): Insert row at correct index. crbug.com/776105. + $('blacklisted-hosts-table').appendChild(row); + }, + + /** + * Update to the page that the user blacklisted status has changed. + * + * @override + * @param {boolean} blacklisted The time of the event in milliseconds since + * Unix epoch. + */ + onUserBlacklistedStatusChange: function(blacklisted) { + let userBlacklistedStatus = $('user-blacklisted-status-value'); + userBlacklistedStatus.textContent = + (blacklisted ? 'Blacklisted' : 'Not blacklisted'); + }, + + /** + * Update the blacklist cleared status on the page. + * + * @override + * @param {number} time The time of the event in milliseconds since Unix + * epoch. + */ + onBlacklistCleared: function(time) { + let blacklistClearedStatus = $('blacklist-last-cleared-time'); + blacklistClearedStatus.textContent = getTimeFormat(time); + + // Remove hosts from table. + let blacklistedHostsTable = $('blacklisted-hosts-table'); + for (let row = blacklistedHostsTable.rows.length - 1; row > 0; row--) { + blacklistedHostsTable.deleteRow(row); + } + + // Remove log message from logs table. + removeAllLogMessagesRows(); + }, + + /** + * Update the page with the new value of ignored blacklist decision status. + * + * @override + * @param {boolean} ignored The new status of whether the previews blacklist + * decisions is blacklisted or not. + */ + onIgnoreBlacklistDecisionStatusChanged: function(ignored) { + let ignoreButton = $('ignore-blacklist-button'); + ignoreButton.textContent = + ignored ? ENABLE_BLACKLIST_BUTTON : IGNORE_BLACKLIST_BUTTON; - logsTable.appendChild(tableRow); + // Update the status of blacklist ignored on the page. + $('blacklist-ignored-status').textContent = + ignored ? IGNORE_BLACKLIST_MESSAGE : ''; + }, + + /** + * Update the page with the new value of estimated Effective Connection Type + * (ECT). Log the ECT to the ECT logs table. + * + * @override + * @param {string} type The string representation of estimated ECT. + */ + onEffectiveConnectionTypeChanged: function(type) { + // Change the current ECT. + let ectType = $('nqe-type'); + ectType.textContent = type; + + let now = getTimeFormat(Date.now()); + + // Log ECT changed event to ECT change log. + let nqeRow = + $('nqe-logs-table').insertRow(1); // Index 0 belongs to header row. + + let timeCol = document.createElement('td'); + timeCol.textContent = now; + timeCol.setAttribute('class', 'nqe-time-column'); + nqeRow.appendChild((timeCol)); + + let nqeCol = document.createElement('td'); + nqeCol.setAttribute('class', 'nqe-value-column'); + nqeCol.textContent = type; + nqeRow.appendChild(nqeCol); + + // Insert ECT changed message to message-logs-table. + insertMessageRowToMessageLogTable( + now, 'ECT Changed', 'Effective Connection Type changed to ' + type, ''); }, }; @@ -96,6 +349,31 @@ cr.define('interventions_internals', () => { function init(handler) { pageHandler = handler; getPreviewsEnabled(); + getPreviewsFlagsDetails(); + + let ignoreButton = $('ignore-blacklist-button'); + ignoreButton.addEventListener('click', () => { + // Whether the blacklist is currently ignored. + let ignored = (ignoreButton.textContent == ENABLE_BLACKLIST_BUTTON); + // Try to reverse the ignore status. + pageHandler.setIgnorePreviewsBlacklistDecision(!ignored); + }); + } + + /** + * Sort keys by the value of each value by its description attribute of a + * |mapObject|. + * + * @param mapObject {!Map<string, Object} A map where all values have a + * description attribute. + * @return A list of keys sorted by their descriptions. + */ + function getSortedKeysByDescription(mapObject) { + let sortedKeys = Array.from(mapObject.keys()); + sortedKeys.sort((a, b) => { + return mapObject.get(a).description > mapObject.get(b).description; + }); + return sortedKeys; } /** @@ -105,17 +383,17 @@ cr.define('interventions_internals', () => { function getPreviewsEnabled() { pageHandler.getPreviewsEnabled() .then((response) => { - let statuses = $('previews-statuses'); + let statuses = $('previews-enabled-status'); - // TODO(thanhdle): The statuses are not printed in alphabetic order of - // the key. crbug.com/772458 - response.statuses.forEach((value, key) => { + getSortedKeysByDescription(response.statuses).forEach((key) => { + let value = response.statuses.get(key); let message = value.description + ': '; message += value.enabled ? 'Enabled' : 'Disabled'; assert(!$(key), 'Component ' + key + ' already existed!'); - let node = document.createElement('p'); + let node = document.createElement('div'); + node.setAttribute('class', 'previews-status-value'); node.setAttribute('id', key); node.textContent = message; statuses.appendChild(node); @@ -126,6 +404,41 @@ cr.define('interventions_internals', () => { }); } + function getPreviewsFlagsDetails() { + pageHandler.getPreviewsFlagsDetails() + .then((response) => { + let flags = $('previews-flags-table'); + + getSortedKeysByDescription(response.flags).forEach((key) => { + let value = response.flags.get(key); + assert(!$(key), 'Component ' + key + ' already existed!'); + + let flagDescription = document.createElement('a'); + flagDescription.setAttribute('class', 'previews-flag-description'); + flagDescription.setAttribute('id', key + 'Description'); + flagDescription.setAttribute('href', value.link); + flagDescription.textContent = value.description; + + let flagNameTd = document.createElement('td'); + flagNameTd.appendChild(flagDescription); + + let flagValueTd = document.createElement('td'); + flagValueTd.setAttribute('class', 'previews-flag-value'); + flagValueTd.setAttribute('id', key + 'Value'); + flagValueTd.textContent = value.value; + + let node = document.createElement('tr'); + node.setAttribute('class', 'previews-flag-container'); + node.appendChild(flagNameTd); + node.appendChild(flagValueTd); + flags.appendChild(node); + }); + }) + .catch((error) => { + console.error(error.message); + }); + } + return { init: init, }; @@ -137,6 +450,8 @@ window.setupFn = window.setupFn || function() { document.addEventListener('DOMContentLoaded', () => { setupTabControl(); + setupLogSearch(); + setupLogClear(); let pageHandler = null; let pageImpl = null; diff --git a/chromium/chrome/browser/resources/local_ntp/local_ntp.css b/chromium/chrome/browser/resources/local_ntp/local_ntp.css index 7c507af4ae1..0df099dc71c 100644 --- a/chromium/chrome/browser/resources/local_ntp/local_ntp.css +++ b/chromium/chrome/browser/resources/local_ntp/local_ntp.css @@ -9,8 +9,17 @@ html { /* We add an extra pixel because rounding errors on different zooms can * make the width shorter than it should be. */ + 1px); + --tile-height: 128px; --tile-margin: 16px; --tile-width: 154px; + /* Two rows of tiles, margin between the rows, and 4px/8px of margin on top + * and bottom respectively. If you change this, also change the corresponding + * values in most_visited_single.css. */ + --mv-tiles-height: calc( + 4px + var(--tile-height) + var(--tile-margin) + var(--tile-height) + 8px); + /* Base height 16px, plus 8px each of padding on top and bottom. */ + --mv-notice-height: calc(8px + 16px + 8px); + height: 100%; } /* width >= (3 cols * (16px + 154px) - 16px + 200px) */ @@ -34,20 +43,31 @@ body { cursor: default; font-family: arial, sans-serif; font-size: small; + height: 100%; margin: 0; overflow-x: hidden; } +/* Button defaults vary by platform. Reset CSS so that the NTP can use buttons + * as a kind of clickable div. */ +button { + background: transparent; + border: 0; + margin: 0; + padding: 0; +} + #ntp-contents { + display: flex; + flex-direction: column; + height: 100%; margin: 0 auto; text-align: -webkit-center; width: var(--content-width); } .non-google-page #ntp-contents { - left: calc(50% - var(--content-width)/2); - position: absolute; - top: calc(50% - 155px); + justify-content: center; } body.hide-fakebox-logo #logo, @@ -79,11 +99,28 @@ body.hide-fakebox-logo #fakebox { #logo-default, #logo-doodle { opacity: 0; + visibility: hidden; +} + +#logo-default.show-logo, +#logo-doodle.show-logo { + opacity: 1; + visibility: visible; +} + +#logo-doodle-button, +#logo-doodle-iframe { + display: none; +} + +#logo-doodle-button.show-logo, +#logo-doodle-iframe.show-logo { + display: block; } #logo-default.fade, #logo-doodle.fade { - transition: opacity 130ms; + transition: opacity 130ms, visibility 130ms; } #logo-default, @@ -125,23 +162,32 @@ body.alternate-logo #logo-non-white { top: 44px; } -#logo-doodle-link { - cursor: pointer; +#logo-doodle-button { margin: 0 auto; } -.non-white-bg #logo-doodle-link { +.non-white-bg #logo-doodle-button, +.non-white-bg #logo-doodle-iframe { display: none; } +#logo-doodle-iframe { + border: 0; + height: 228px; + width: 100%; +} + #logo-doodle-notifier { display: none; } .non-white-bg #logo-doodle-notifier { + background: transparent; + border: 0; cursor: pointer; display: inline-block; height: 24px; left: 148px; + padding: 0; position: relative; top: 100px; width: 24px; @@ -213,6 +259,7 @@ body.alternate-logo #logo-non-white { font-size: 18px; height: 44px; line-height: 36px; + margin-bottom: 8px; max-width: 672px; outline: none; position: relative; @@ -287,12 +334,13 @@ html[dir=rtl] #fakebox-cursor { #fakebox-microphone { background: url(googlemic_clr_24px.svg) no-repeat center; background-size: 24px 24px; + bottom: 0; cursor: pointer; - height: 21px; padding: 22px 12px 0; position: absolute; right: 0; - width: 17px; + top: 0; + width: 41px; } html[dir=rtl] #fakebox-microphone { @@ -324,7 +372,10 @@ body.fakebox-focused #fakebox-cursor { } #most-visited { - margin-top: 64px; + height: 100%; + margin-top: 56px; + max-height: calc(var(--mv-tiles-height) + var(--mv-notice-height)); + overflow: hidden; text-align: -webkit-center; user-select: none; } @@ -335,11 +386,10 @@ body.fakebox-focused #fakebox-cursor { } #mv-tiles { - /* Two rows of tiles of 128px each, 16px of spacing between the rows, and - * 4px/8px of margin on top and bottom respectively. If you change this, also - * change the corresponding values in most_visited_single.css. */ - height: calc(2*128px + 16px + 4px + 8px); + height: var(--mv-tiles-height); margin: 0; + /* Clamp to the remaining window height minus space for #mv-notice. */ + max-height: calc(100% - var(--mv-notice-height)); position: relative; text-align: -webkit-auto; } @@ -379,7 +429,7 @@ html[dir=rtl] #mv-notice-x { font-size: 12px; font-weight: bold; opacity: 1; - padding: 10px 0; + padding: 8px 0; } #mv-notice span { @@ -450,7 +500,9 @@ html[dir=rtl] #attribution, } #one-google { - position: fixed; + position: absolute; + right: 0; + top: 0; transition: opacity 130ms; z-index: 1; } diff --git a/chromium/chrome/browser/resources/local_ntp/local_ntp.html b/chromium/chrome/browser/resources/local_ntp/local_ntp.html index 82ed2ab45ba..7d1111c55c2 100644 --- a/chromium/chrome/browser/resources/local_ntp/local_ntp.html +++ b/chromium/chrome/browser/resources/local_ntp/local_ntp.html @@ -32,16 +32,17 @@ <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"> + <button id="logo-doodle-button"> <img id="logo-doodle-image"></img> - </a> + </button> + <iframe id="logo-doodle-iframe"></iframe> <!-- A spinner, visible on dark-themed NTPs, prompting the doodle --> - <div id="logo-doodle-notifier"> + <button 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> + </button> </div> </div> <div id="fakebox"> @@ -49,7 +50,7 @@ <input id="fakebox-input" autocomplete="off" tabindex="-1" type="url" aria-hidden="true"> <div id="fakebox-cursor"></div> - <div id="fakebox-microphone" tabindex="1" hidden></div> + <button id="fakebox-microphone" hidden></button> </div> </div> <div id="most-visited"> @@ -60,9 +61,9 @@ <span id="mv-msg"></span> <!-- Links in the notification. --> <span id="mv-notice-links"> - <span id="mv-undo" tabindex="2"></span> - <span id="mv-restore" tabindex="2"></span> - <div id="mv-notice-x" tabindex="2"></div> + <span id="mv-undo" tabindex="0"></span> + <span id="mv-restore" tabindex="0"></span> + <div id="mv-notice-x" tabindex="0"></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 cabafe39fd5..d74320f6cfd 100644 --- a/chromium/chrome/browser/resources/local_ntp/local_ntp.js +++ b/chromium/chrome/browser/resources/local_ntp/local_ntp.js @@ -9,6 +9,15 @@ /** + * Whether the most visited tiles have finished loading, i.e. we've received the + * 'loaded' postMessage from the iframe. Used by tests to detect that loading + * has completed. + * @type {boolean} + */ +var tilesAreLoaded = false; + + +/** * Controls rendering the new tab page for InstantExtended. * @return {Object} A limited interface for testing the local NTP. */ @@ -68,7 +77,8 @@ var CLASSES = { // 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. + RTL: 'rtl', // Right-to-left language text. + SHOW_LOGO: 'show-logo', // Marks logo/doodle that should be shown. }; @@ -89,7 +99,8 @@ var IDS = { LOGO_DEFAULT: 'logo-default', LOGO_DOODLE: 'logo-doodle', LOGO_DOODLE_IMAGE: 'logo-doodle-image', - LOGO_DOODLE_LINK: 'logo-doodle-link', + LOGO_DOODLE_IFRAME: 'logo-doodle-iframe', + LOGO_DOODLE_BUTTON: 'logo-doodle-button', LOGO_DOODLE_NOTIFIER: 'logo-doodle-notifier', NOTIFICATION: 'mv-notice', NOTIFICATION_CLOSE_BUTTON: 'mv-notice-x', @@ -103,6 +114,18 @@ var IDS = { /** + * Counterpart of search_provider_logos::LogoType. + * @enum {string} + * @const + */ +var LOGO_TYPE = { + SIMPLE: 'SIMPLE', + ANIMATED: 'ANIMATED', + INTERACTIVE: 'INTERACTIVE', +}; + + +/** * 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. @@ -532,6 +555,7 @@ function handlePostMessage(event) { var cmd = event.data.cmd; var args = event.data; if (cmd == 'loaded') { + tilesAreLoaded = true; 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. @@ -656,6 +680,9 @@ function init() { // Got a (possibly empty) ddl object. Show logo or doodle. showLogoOrDoodle( ddl.image || null, ddl.metadata || null, /*fromCache=*/true); + // Never hide an interactive doodle if it was already shown. + if (ddl.metadata && (ddl.metadata.type === LOGO_TYPE.INTERACTIVE)) + return; // If we got a valid ddl object (from cache), load a fresh one. if (ddl.v !== null) { loadDoodle(ddl.v, function(ddl) { @@ -675,6 +702,9 @@ function init() { state.notheme = true; window.history.replaceState(state, document.title); onThemeChange(); + if (e.detail === 0) { // Activated by keyboard. + $(IDS.LOGO_DOODLE_BUTTON).focus(); + } }); } else { document.body.classList.add(CLASSES.NON_GOOGLE_PAGE); @@ -702,7 +732,8 @@ function init() { // Create the most visited iframe. var iframe = document.createElement('iframe'); iframe.id = IDS.TILES_IFRAME; - iframe.tabIndex = 1; + iframe.name = IDS.TILES_IFRAME; + iframe.title = configData.translatedStrings.mostVisitedTitle; iframe.src = 'chrome-search://most-visited/single.html?' + args.join('&'); $(IDS.TILES).appendChild(iframe); @@ -784,16 +815,6 @@ var loadDoodle = function(v, onload) { }; -/** 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. @@ -804,39 +825,54 @@ var isFadedOut = function(element) { * @returns {boolean} */ var isDoodleCurrentlyVisible = function(image, metadata) { - var haveDoodle = ($(IDS.LOGO_DOODLE).style.opacity != 0); + var haveDoodle = ($(IDS.LOGO_DOODLE).classList.contains(CLASSES.SHOW_LOGO)); 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); + if (metadata.type === LOGO_TYPE.INTERACTIVE) { + var logoDoodleIframe = $(IDS.LOGO_DOODLE_IFRAME); + return logoDoodleIframe.classList.contains(CLASSES.SHOW_LOGO) && + (logoDoodleIframe.src === metadata.fullPageUrl); + } else { + var logoDoodleImage = $(IDS.LOGO_DOODLE_IMAGE); + var logoDoodleButton = $(IDS.LOGO_DOODLE_BUTTON); + return logoDoodleButton.classList.contains(CLASSES.SHOW_LOGO) && + ((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); + if (metadata.type === LOGO_TYPE.INTERACTIVE) { + $(IDS.LOGO_DOODLE_BUTTON).classList.remove(CLASSES.SHOW_LOGO); + $(IDS.LOGO_DOODLE_IFRAME).classList.add(CLASSES.SHOW_LOGO); + } else { + $(IDS.LOGO_DOODLE_IMAGE).src = image; + $(IDS.LOGO_DOODLE_BUTTON).classList.add(CLASSES.SHOW_LOGO); + $(IDS.LOGO_DOODLE_IFRAME).classList.remove(CLASSES.SHOW_LOGO); + + 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); + } + $(IDS.LOGO_DOODLE).classList.add(CLASSES.SHOW_LOGO); } else { - $(IDS.LOGO_DEFAULT).style.opacity = 1; + $(IDS.LOGO_DEFAULT).classList.add(CLASSES.SHOW_LOGO); } }; /** 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 + * After a logo fades out, onDoodleFadeOutComplete fades in a logo according to * targetDoodle. */ var targetDoodle = { @@ -852,14 +888,18 @@ var targetDoodle = { * @param {HTMLElement} element */ var startFadeOut = function(element) { + if (!element.classList.contains(CLASSES.SHOW_LOGO)) { + return; + } + // 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; + element.classList.remove(CLASSES.SHOW_LOGO); + element.addEventListener('transitionend', onDoodleFadeOutComplete); }; @@ -886,51 +926,55 @@ var fadeToLogoOrDoodle = function(image, metadata) { targetDoodle.image = image; targetDoodle.metadata = metadata; - // Start fading out the current logo or doodle. onDoodleTransitionEnd will + // Start fading out the current logo or doodle. onDoodleFadeOutComplete 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); +var onDoodleFadeOutComplete = function(e) { + // Fade-out finished. Start fading in the appropriate logo. + $(IDS.LOGO_DOODLE).classList.add(CLASSES.FADE); + $(IDS.LOGO_DEFAULT).classList.add(CLASSES.FADE); + showLogoOrDoodle( + targetDoodle.image, targetDoodle.metadata, /*fromCache=*/false); - logoDefault.removeEventListener('transitionend', onDoodleTransitionEnd); - logoDoodle.removeEventListener('transitionend', onDoodleTransitionEnd); - } + this.removeEventListener('transitionend', onDoodleFadeOutComplete); }; var applyDoodleMetadata = function(metadata) { - var logoDoodleLink = $(IDS.LOGO_DOODLE_LINK); + var logoDoodleButton = $(IDS.LOGO_DOODLE_BUTTON); 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); + var logoDoodleIframe = $(IDS.LOGO_DOODLE_IFRAME); + + switch (metadata.type) { + case LOGO_TYPE.SIMPLE: + logoDoodleImage.title = metadata.altText; + logoDoodleButton.onclick = function() { + ntpApiHandle.logEvent(LOG_TYPE.NTP_STATIC_LOGO_CLICKED); + window.location = metadata.onClickUrl; }; - }; - } else { - logoDoodleLink.href = metadata.onClickUrl; - logoDoodleLink.onclick = function() { - ntpApiHandle.logEvent(LOG_TYPE.NTP_STATIC_LOGO_CLICKED); - }; + break; + + case LOGO_TYPE.ANIMATED: + logoDoodleImage.title = metadata.altText; + logoDoodleButton.onclick = function(e) { + ntpApiHandle.logEvent(LOG_TYPE.NTP_CTA_LOGO_CLICKED); + e.preventDefault(); + logoDoodleImage.src = metadata.animatedUrl; + logoDoodleButton.onclick = function() { + ntpApiHandle.logEvent(LOG_TYPE.NTP_ANIMATED_LOGO_CLICKED); + window.location = metadata.onClickUrl; + }; + }; + break; + + case LOGO_TYPE.INTERACTIVE: + logoDoodleIframe.title = metadata.altText; + logoDoodleIframe.src = metadata.fullPageUrl; + break; } }; diff --git a/chromium/chrome/browser/resources/local_ntp/most_visited_single.css b/chromium/chrome/browser/resources/local_ntp/most_visited_single.css index 5035a1789e1..b2128fc4837 100644 --- a/chromium/chrome/browser/resources/local_ntp/most_visited_single.css +++ b/chromium/chrome/browser/resources/local_ntp/most_visited_single.css @@ -3,15 +3,19 @@ * found in the LICENSE file. */ html { + /* Constants. */ --tile-height: 128px; --tile-margin: 16px; --tile-width: 154px; --title-height: 32px; + + /* May be overridden by themes (on the body element). */ + --tile-title-color: #323232; } body { background: none transparent; - color: #323232; + color: var(--tile-title-color); margin: 0; overflow: hidden; padding: 0; @@ -44,7 +48,7 @@ a:visited { * local_ntp.css. */ height: calc(2*var(--tile-height) + var(--tile-margin)); line-height: calc(var(--tile-height) + var(--tile-margin)); - margin: 4px 0 calc(var(--tile-margin) / 2) 0; + margin: 4px 0 8px 0; opacity: 0; position: absolute; /* This align correctly for both LTR and RTL */ @@ -64,22 +68,55 @@ a:visited { line-height: 100%; margin: 0 calc(var(--tile-margin) / 2); opacity: 1; - overflow: hidden; position: relative; vertical-align: top; white-space: nowrap; width: var(--tile-width); } -/* Minimal layout: 2 columns; only first 4 tiles are visible. */ -.mv-tile:nth-child(-n+4), -.mv-empty-tile:nth-child(-n+4) { - display: inline-block; +/* Min height for showing 1 row: 4px + 128px + 8px + Min height for showing 2 rows: 4px + 128px + 16px + 128px + 8px + In both cases, give it half a px of tolerance, because otherwise rounding + errors at some zoom levels break this and a row sometimes doesn't show up. +*/ + +/* Minimal layout: 1 row, 2 columns; only first 2 tiles are visible. */ +@media (min-height: 139.5px) { + .mv-tile:nth-child(-n+2), + .mv-empty-tile:nth-child(-n+2) { + display: inline-block; + } +} + +/* width >= (3 cols * (16px + 154px)) + * 1 row, 3 columns; first 3 tiles are visible. */ +@media (min-height: 139.5px) and (min-width: 510px) { + .mv-tile:nth-child(-n+3), + .mv-empty-tile:nth-child(-n+3) { + display: inline-block; + } +} + +/* width >= (4 cols * (16px + 154px)) + * 1 row, 4 columns; first 4 tiles are visible. */ +@media (min-height: 139.5px) and (min-width: 680px) { + .mv-tile:nth-child(-n+4), + .mv-empty-tile:nth-child(-n+4) { + display: inline-block; + } +} + +/* 2 rows, 2 columns; only first 4 tiles are visible. */ +@media (min-height: 283.5px) { + .mv-tile:nth-child(-n+4), + .mv-empty-tile:nth-child(-n+4) { + display: inline-block; + } } /* width >= (3 cols * (16px + 154px)) - * 3 columns; first 6 tiles are visible. */ -@media (min-width: 510px) { + * 2 rows, 3 columns; first 6 tiles are visible. */ +@media (min-height: 283.5px) and (min-width: 510px) { .mv-tile:nth-child(-n+6), .mv-empty-tile:nth-child(-n+6) { display: inline-block; @@ -87,8 +124,8 @@ a:visited { } /* width >= (4 cols * (16px + 154px)) - * 4 columns; first 8 tiles are visible. */ -@media (min-width: 680px) { + * 2 rows, 4 columns; first 8 tiles are visible. */ +@media (min-height: 283.5px) and (min-width: 680px) { .mv-tile:nth-child(-n+8), .mv-empty-tile:nth-child(-n+8) { display: inline-block; @@ -103,6 +140,11 @@ a:visited { background: rgb(245,245,245); } +body.dark-theme .mv-tile, +body.dark-theme .mv-empty-tile { + background: rgb(51,51,51); +} + .mv-tile { box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08); cursor: pointer; @@ -111,17 +153,19 @@ a:visited { transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } -.mv-tile:hover:not(:active) { +.mv-tile:hover:not(:active), +.mv-tile:focus-within:not(:active) { box-shadow: 0 3px 8px 0 rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.08); } -.mv-tile:focus { - -webkit-filter: brightness(92%); +.mv-tile:focus, +.mv-tile:focus-within { + filter: brightness(92%); } .mv-tile:active { - -webkit-filter: brightness(88%); box-shadow: 0 3px 8px 0 rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.12); + filter: brightness(88%); } .mv-tile.blacklisted { @@ -168,6 +212,7 @@ html[dir=rtl] .mv-title[style*='direction: rtl'] { } .mv-thumb { + border-radius: 0 0 2px 2px; cursor: pointer; display: block; height: calc(var(--tile-height) - var(--title-height)); @@ -187,6 +232,10 @@ html[dir=rtl] .mv-title[style*='direction: rtl'] { background-color: rgb(245,245,245); } +body.dark-theme .mv-thumb.failed-img { + background-color: #555; +} + /* We use ::after without content to provide an aditional element on top of the * thumbnail. */ .mv-thumb.failed-img::after { @@ -199,42 +248,62 @@ html[dir=rtl] .mv-title[style*='direction: rtl'] { width: 0; } +body.dark-theme .mv-thumb.failed-img::after { + border-color: #333; +} + .mv-x { background: linear-gradient(to left, rgb(250,250,250) 60%, transparent); border: none; cursor: pointer; - height: 30px; + height: var(--title-height); opacity: 0; + padding: 0; position: absolute; right: 0; transition: opacity 150ms; width: 40px; } +body.dark-theme .mv-x { + background: linear-gradient(to left, rgb(51,51,51) 60%, transparent); +} + /* We use ::after without content to provide the masked X element. The "bottom" * div is actually just the gradient. */ .mv-x::after { + --mask-offset: calc((var(--title-height) - var(--mask-width)) / 2); + --mask-width: 10px; -webkit-mask-image: -webkit-image-set( url(chrome-search://local-ntp/images/close_3_mask.png) 1x, url(chrome-search://local-ntp/images/close_3_mask.png@2x) 2x); - -webkit-mask-position: 12px 10px; + -webkit-mask-position: var(--mask-offset) var(--mask-offset); -webkit-mask-repeat: no-repeat; - -webkit-mask-size: 10px 10px; + -webkit-mask-size: var(--mask-width); background-color: rgba(90,90,90,0.7); content: ''; display: block; height: var(--title-height); position: absolute; right: 0; + top: 0; width: var(--title-height); } +body.dark-theme .mv-x.mv-x::after { + background-color: rgba(255,255,255,0.7); +} + html[dir=rtl] .mv-x { background: linear-gradient(to right, rgb(250,250,250) 60%, transparent); left: -1px; right: auto; } +body.dark-theme body.dark-theme .mv-x { + background: linear-gradient(to right, rgb(51,51,51) 60%, transparent); +} + html[dir=rtl] .mv-x::after { left: -1px; right: auto; @@ -244,16 +313,27 @@ html[dir=rtl] .mv-x::after { background-color: rgb(90,90,90); } +body.dark-theme .mv-x:hover::after { + background-color: #fff; +} + .mv-x:active::after { background-color: rgb(66,133,244); } -.mv-tile:hover .mv-x { +body.dark-theme .mv-x:active::after { + background-color: rgba(255,255,255,0.5); +} + +.mv-tile:hover .mv-x, +.mv-tile:focus .mv-x { opacity: 1; transition-delay: 500ms; } -.mv-x:hover { +.mv-x:hover, +.mv-x:focus { + opacity: 1; transition: none; } diff --git a/chromium/chrome/browser/resources/local_ntp/most_visited_single.html b/chromium/chrome/browser/resources/local_ntp/most_visited_single.html index 2b1ba59971e..094e8f14537 100644 --- a/chromium/chrome/browser/resources/local_ntp/most_visited_single.html +++ b/chromium/chrome/browser/resources/local_ntp/most_visited_single.html @@ -7,7 +7,6 @@ <base target="_top"> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="single.css"> - <style type="text/css" id="custom-theme"></style> <script src="single.js"></script> </head> <body> 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 446873a2ce6..ca988627132 100644 --- a/chromium/chrome/browser/resources/local_ntp/most_visited_single.js +++ b/chromium/chrome/browser/resources/local_ntp/most_visited_single.js @@ -166,7 +166,8 @@ var countLoad = function() { swapInNewTiles(); logEvent(LOG_TYPE.NTP_ALL_TILES_LOADED); window.parent.postMessage({cmd: 'loaded'}, DOMAIN_ORIGIN); - // TODO(treib): Why do we reset to 1 here? + // Reset to 1, so that any further 'show' message will cause us to swap in + // fresh tiles. loadedCounter = 1; } }; @@ -222,41 +223,8 @@ var showTiles = function(info) { * @param {object} info Data received in the message. */ var updateTheme = function(info) { - var themeStyle = []; - - if (info.isThemeDark) { - themeStyle.push( - '.mv-tile, .mv-empty-tile { ' + - 'background: rgb(51,51,51); }'); - themeStyle.push( - '.mv-thumb.failed-img { ' + - 'background-color: #555; }'); - themeStyle.push( - '.mv-thumb.failed-img::after { ' + - 'border-color: #333; }'); - themeStyle.push( - '.mv-x { ' + - 'background: linear-gradient(to left, ' + - 'rgb(51,51,51) 60%, transparent); }'); - themeStyle.push( - 'html[dir=rtl] .mv-x { ' + - 'background: linear-gradient(to right, ' + - 'rgb(51,51,51) 60%, transparent); }'); - themeStyle.push( - '.mv-x::after { ' + - 'background-color: rgba(255,255,255,0.7); }'); - themeStyle.push( - '.mv-x:hover::after { ' + - 'background-color: #fff; }'); - themeStyle.push( - '.mv-x:active::after { ' + - 'background-color: rgba(255,255,255,0.5); }'); - } - if (info.tileTitleColor) { - themeStyle.push('body { color: ' + info.tileTitleColor + '; }'); - } - - document.querySelector('#custom-theme').textContent = themeStyle.join('\n'); + document.body.style.setProperty('--tile-title-color', info.tileTitleColor); + document.body.classList.toggle('dark-theme', info.isThemeDark); }; @@ -399,7 +367,7 @@ var renderTile = function(data) { var html = []; html.push('<div class="mv-favicon"></div>'); html.push('<div class="mv-title"></div><div class="mv-thumb"></div>'); - html.push('<div class="mv-x" role="button"></div>'); + html.push('<button class="mv-x"></button>'); tile.innerHTML = html.join(''); tile.lastElementChild.title = queryArgs['removeTooltip'] || ''; @@ -496,8 +464,9 @@ var renderTile = function(data) { var favicon = tile.querySelector('.mv-favicon'); var fi = document.createElement('img'); fi.src = data.faviconUrl; - // Set the title to empty so screen readers won't say the image name. + // Set title and alt to empty so screen readers won't say the image name. fi.title = ''; + fi.alt = ''; loadedCounter += 1; fi.addEventListener('load', countLoad); fi.addEventListener('error', countLoad); @@ -514,6 +483,12 @@ var renderTile = function(data) { ev.stopPropagation(); }); + // Don't allow the event to bubble out to the containing tile, as that would + // trigger navigation to the tile URL. + mvx.addEventListener('keydown', function(event) { + event.stopPropagation(); + }); + return tile; }; diff --git a/chromium/chrome/browser/resources/local_ntp/voice.css b/chromium/chrome/browser/resources/local_ntp/voice.css index bcd24e9fa0e..891d91f7c96 100644 --- a/chromium/chrome/browser/resources/local_ntp/voice.css +++ b/chromium/chrome/browser/resources/local_ntp/voice.css @@ -24,7 +24,6 @@ /* Color constants. */ :root { - --dark_grey: #dbdbdb; --dark_red: rgb(205, 0, 0); --grey: #777; --light_grey: #eee; @@ -33,6 +32,7 @@ --active_icon_color: white; --button_shadow: rgba(0, 0, 0, .1); --inactive_icon_color: #999; + --level_animation_color: #dbdbdb; --listening_icon_color: var(--light_red); --text_link_color: rgb(17, 85, 204); } @@ -64,13 +64,13 @@ /* The close 'x' button. */ .close-button { - color: var(--grey); + color: black; cursor: pointer; font-size: 26px; height: 11px; line-height: 15px; margin: 15px; - opacity: .6; + opacity: .54; padding: 0; position: absolute; right: 0; @@ -84,11 +84,11 @@ html[dir=rtl] .close-button { } .close-button:hover { - opacity: .8; + opacity: .66; } .close-button:active { - opacity: 1; + opacity: .78; } /* The vertical positioning container. */ @@ -172,7 +172,7 @@ html[dir=rtl] .close-button { /* Vibrating input volume level. */ .level { - background-color: var(--dark_grey); + background-color: var(--level_animation_color); border-radius: 100%; display: inline-block; height: 301px; diff --git a/chromium/chrome/browser/resources/md_bookmarks/OWNERS b/chromium/chrome/browser/resources/md_bookmarks/OWNERS index b02ce2c777a..267acc634e7 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/OWNERS +++ b/chromium/chrome/browser/resources/md_bookmarks/OWNERS @@ -1,4 +1,3 @@ calamity@chromium.org -tsergeant@chromium.org # COMPONENT: UI>Browser>Bookmarks diff --git a/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.js b/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.js index f4da5779d79..330905b542c 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.js +++ b/chromium/chrome/browser/resources/md_bookmarks/dnd_manager.js @@ -330,6 +330,7 @@ cr.define('bookmarks', function() { */ update: function(dropDest) { this.timerProxy.clearTimeout(this.removeDropIndicatorTimeoutId_); + this.removeDropIndicatorTimeoutId_ = null; const indicatorElement = dropDest.element.getDropTarget(); const position = dropDest.position; @@ -342,9 +343,11 @@ cr.define('bookmarks', function() { * Stop displaying the drop indicator. */ finish: function() { + if (this.removeDropIndicatorTimeoutId_) + return; + // The use of a timeout is in order to reduce flickering as we move // between valid drop targets. - this.timerProxy.clearTimeout(this.removeDropIndicatorTimeoutId_); this.removeDropIndicatorTimeoutId_ = this.timerProxy.setTimeout(() => { this.removeDropIndicatorStyle(); }, 100); diff --git a/chromium/chrome/browser/resources/md_bookmarks/list.html b/chromium/chrome/browser/resources/md_bookmarks/list.html index 2f5ed7aa577..4c3ec439e4d 100644 --- a/chromium/chrome/browser/resources/md_bookmarks/list.html +++ b/chromium/chrome/browser/resources/md_bookmarks/list.html @@ -15,7 +15,7 @@ <style include="shared-style"> :host { overflow-y: auto; - padding: 20px var(--card-padding-side) 20px + padding: 24px var(--card-padding-side) 24px calc(var(--card-padding-side) - var(--splitter-width)); } diff --git a/chromium/chrome/browser/resources/md_downloads/downloads.html b/chromium/chrome/browser/resources/md_downloads/downloads.html index 789c1d5fdcf..bc126bb68c6 100644 --- a/chromium/chrome/browser/resources/md_downloads/downloads.html +++ b/chromium/chrome/browser/resources/md_downloads/downloads.html @@ -7,7 +7,7 @@ <style> html { --downloads-card-margin: 24px; - --downloads-card-width: 640px; + --downloads-card-width: 680px; background: #f1f1f1; } diff --git a/chromium/chrome/browser/resources/md_downloads/item.js b/chromium/chrome/browser/resources/md_downloads/item.js index d589ab8cadd..da53e333d3f 100644 --- a/chromium/chrome/browser/resources/md_downloads/item.js +++ b/chromium/chrome/browser/resources/md_downloads/item.js @@ -119,7 +119,7 @@ cr.define('downloads', function() { if (!this.data.by_ext_id || !this.data.by_ext_name) return ''; - const url = 'chrome://extensions#' + this.data.by_ext_id; + const url = `chrome://extensions#${this.data.by_ext_id}`; const name = this.data.by_ext_name; return loadTimeData.getStringF('controlledByUrl', url, HTMLEscape(name)); }, @@ -260,8 +260,8 @@ cr.define('downloads', function() { } else { this.$.url.href = assert(this.data.url); const filePath = encodeURIComponent(this.data.file_path); - const scaleFactor = '?scale=' + window.devicePixelRatio + 'x'; - this.$['file-icon'].src = 'chrome://fileicon/' + filePath + scaleFactor; + const scaleFactor = `?scale=${window.devicePixelRatio}x`; + this.$['file-icon'].src = `chrome://fileicon/${filePath}${scaleFactor}`; } }, diff --git a/chromium/chrome/browser/resources/md_downloads/toolbar.html b/chromium/chrome/browser/resources/md_downloads/toolbar.html index 2985c1d09ed..ce6dee4341d 100644 --- a/chromium/chrome/browser/resources/md_downloads/toolbar.html +++ b/chromium/chrome/browser/resources/md_downloads/toolbar.html @@ -25,7 +25,6 @@ } #toolbar { - --cr-toolbar-field-width: var(--downloads-card-width); flex: 1; } diff --git a/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp b/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp index 7689bd4eb76..6c34d5b18fc 100644 --- a/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp @@ -21,6 +21,7 @@ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(EXTERNS_GYP):developer_private', 'item', + 'item_behavior', 'item_util', 'navigation_helper', ], @@ -32,6 +33,7 @@ '<(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', + '<(EXTERNS_GYP):developer_private', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -48,9 +50,10 @@ { 'target_name': 'error_page', 'dependencies': [ + '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-menu/compiled_resources2.gyp:paper-menu-extracted', '<(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', + '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:focus_outline_manager', '<(EXTERNS_GYP):developer_private', 'code_section', 'item_util', @@ -59,11 +62,9 @@ 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'extensions', + 'target_name': 'install_warnings_dialog', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', - 'manager', - 'service', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -75,19 +76,30 @@ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(EXTERNS_GYP):developer_private', + 'item_behavior', 'item_util', 'navigation_helper', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { + 'target_name': 'item_behavior', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(EXTERNS_GYP):developer_private', + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'item_list', 'dependencies': [ + '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-a11y-announcer/compiled_resources2.gyp:iron-a11y-announcer-extracted', '<(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', '<(EXTERNS_GYP):developer_private', + '<(EXTERNS_GYP):metrics_private', 'item', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], @@ -110,6 +122,8 @@ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(EXTERNS_GYP):developer_private', + '<(EXTERNS_GYP):metrics_private', + 'item_behavior', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -117,6 +131,7 @@ 'target_name': 'kiosk_browser_proxy', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + 'item_behavior', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -144,7 +159,6 @@ '<(DEPTH)/ui/webui/resources/cr_elements/cr_drawer/compiled_resources2.gyp:cr_drawer', '<(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', '<(EXTERNS_GYP):developer_private', 'detail_view', 'item', @@ -154,6 +168,7 @@ 'keyboard_shortcuts', 'kiosk_browser_proxy', 'navigation_helper', + 'service', 'sidebar', 'toolbar', 'view_manager', @@ -175,6 +190,7 @@ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(EXTERNS_GYP):developer_private', + 'item_behavior', 'navigation_helper', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], @@ -195,10 +211,10 @@ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', '<(EXTERNS_GYP):developer_private', '<(EXTERNS_GYP):management', + '<(EXTERNS_GYP):metrics_private', 'error_page', 'item', 'load_error', - 'manager', 'navigation_helper', 'pack_dialog', 'toolbar', @@ -229,6 +245,7 @@ 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(EXTERNS_GYP):metrics_private', 'navigation_helper', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], @@ -238,6 +255,7 @@ 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + '<(EXTERNS_GYP):metrics_private', ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, diff --git a/chromium/chrome/browser/resources/md_extensions/detail_view.html b/chromium/chrome/browser/resources/md_extensions/detail_view.html index c66a6a5eb6d..3be69b952af 100644 --- a/chromium/chrome/browser/resources/md_extensions/detail_view.html +++ b/chromium/chrome/browser/resources/md_extensions/detail_view.html @@ -12,17 +12,21 @@ <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-flex-layout/iron-flex-layout-classes.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://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> -<link rel="import" href="strings.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/shadow.html"> +<link rel="import" href="item_behavior.html"> <link rel="import" href="item_util.html"> <link rel="import" href="navigation_helper.html"> +<link rel="import" href="strings.html"> +<link rel="import" href="toggle_row.html"> <dom-module id="extensions-detail-view"> <template> - <style include="iron-flex cr-shared-style cr-icons action-link - paper-button-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; @@ -37,16 +41,26 @@ -webkit-margin-end: 20px; } + #enable-section span { + color: var(--paper-grey-600); + font-weight: 500; + } + + #enable-section .enabled-text { + color: var(--google-blue-500); + } + #container { height: 100%; overflow: overlay; } #main { + @apply(--shadow-elevation-2dp); background-color: white; margin: auto; min-height: 100%; - width: 640px; + width: 680px; } #top-bar { @@ -58,8 +72,8 @@ } #icon { - -webkit-margin-end: 8px; - -webkit-margin-start: 20px; + -webkit-margin-end: 12px; + -webkit-margin-start: 16px; height: 24px; width: 24px; } @@ -69,15 +83,13 @@ @apply(--cr-title-text); } - .control-line { - align-items: center; - display: flex; - justify-content: space-between; - width: 100%; + #learn-more-link { + color: var(--google-blue-700); + text-decoration: none; } - .control-line span { - color: var(--paper-grey-900); + .three-line { + min-height: var(--cr-section-three-line-min-height); } .section { @@ -94,6 +106,10 @@ border-top: none; } + .section.control-line { + justify-content: space-between; + } + .section.continuation.warning { padding-left: 21px; padding-right: 8px; @@ -104,7 +120,6 @@ } .section-title { - color: var(--paper-grey-900); margin-bottom: 12px; } @@ -150,34 +165,64 @@ margin: 0; } - #remove-extension { + button[is='cr-link-row'] { width: 100%; } + + #options-section .control-line:first-child { + border-top: var(--cr-separator-line); + } + + #load-path { + word-break: break-all; + } + + #load-path > a[is='action-link'] { + display: inline; + } + + #size { + display: flex; + align-items: center; + } + + paper-spinner-lite { + @apply(--cr-icon-height-width); + } </style> <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" + aria-label="$i18n{back}" class="icon-arrow-back no-overlap" on-tap="onCloseButtonTap_"></button> - <img alt="" id="icon" src="[[data.iconUrl]]"> + <img id="icon" src="[[data.iconUrl]]" + alt$="[[appOrExtension( + data.type, + '$i18nPolymer{appIcon}', + '$i18nPolymer{extensionIcon}')]]"> <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 class="section continuation control-line" id="enable-section"> + <span class$="{{computeEnabledStyle_(data.state)}}"> + [[computeEnabledText_(data.state)]] + </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" + aria-label$="[[appOrExtension( + data.type, + '$i18nPolymer{appEnabled}', + '$i18nPolymer{extensionEnabled}')]]" + aria-describedby="name" + checked="[[isEnabled_(data.state)]]" + on-change="onEnableChange_" + disabled="[[!isEnableToggleEnabled_(data.*)]]"> + </cr-toggle> </div> </div> <div id="warnings" hidden$="[[!hasWarnings_(data.*)]]"> @@ -185,7 +230,13 @@ hidden$="[[!data.disableReasons.suspiciousInstall]]"> <div> <iron-icon class="warning-icon" icon="cr:warning"></iron-icon> - <span>$i18n{itemSuspiciousInstall}</span> + <span> + $i18n{itemSuspiciousInstall} + <a target="_blank" id="learn-more-link" + href="$i18n{suspiciousInstallHelpUrl}"> + $i18n{learnMore} + </a> + </span> </div> </div> <div class="section continuation warning" id="corrupted-warning" @@ -216,42 +267,60 @@ </div> <div class="section continuation block"> <div class="section-title">$i18n{itemDescriptionLabel}</div> - <div class="section-content">[[data.description]]</div> + <div class="section-content"> + [[getDescription_(data.description, '$i18nPolymer{noDescription}')]] + </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"> + <div class="section-title">$i18n{itemSize}</div> + <div class="section-content" id="size"> + <span>[[size_]]</span> + <paper-spinner-lite active="[[!size_]]" hidden="[[size_]]"> + </paper-spinner-lite> + </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> + <template is="dom-if" if="[[inDevMode]]"> + <div class="section block" id="inspectable-views" + hidden="[[!data.views.length]]"> + <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> </div> - </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)]]"> + <span id="no-permissions" hidden="[[data.permissions.length]]"> $i18n{itemPermissionsEmpty} </span> - <ul id="permissions-list" - hidden$="[[!hasPermissions_(data.permissions.splices)]]"> + <ul id="permissions-list" hidden="[[!data.permissions.length]]"> <template is="dom-repeat" items="[[data.permissions]]"> - <li>[[item]]</li> + <li> + [[item.message]] + <ul hidden="[[!item.submessages.length]]"> + <template is="dom-repeat" items="[[item.submessages]]"> + <li>[[item]]</li> + </template> + </ul> + </li> </template> </ul> </div> @@ -270,60 +339,73 @@ </div> </template> <template is="dom-if" if="[[shouldShowOptionsSection_(data.*)]]"> - <div class="section"> + <div id="options-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> + <extensions-toggle-row id="allow-incognito" class="three-line" + checked="[[data.incognitoAccess.isActive]]" + on-change="onAllowIncognitoChange_"> + <div> + <div>$i18n{itemAllowIncognito}</div> + <div class="section-content">$i18n{incognitoInfoWarning}</div> + </div> + </extensions-toggle-row> </template> <template is="dom-if" if="[[data.fileAccess.isEnabled]]"> - <div class="control-line"> + <extensions-toggle-row id="allow-on-file-urls" + checked="[[data.fileAccess.isActive]]" + on-change="onAllowOnFileUrlsChange_"> <span>$i18n{itemAllowOnFileUrls}</span> - <cr-toggle id="allow-on-file-urls" - checked="[[data.fileAccess.isActive]]" - on-change="onAllowOnFileUrlsChange_"></cr-toggle> - </div> + </extensions-toggle-row> </template> <template is="dom-if" if="[[data.runOnAllUrls.isEnabled]]"> - <div class="control-line"> + <extensions-toggle-row id="allow-on-all-sites" + checked="[[data.runOnAllUrls.isActive]]" + on-change="onAllowOnAllSitesChange_"> <span>$i18n{itemAllowOnAllSites}</span> - <cr-toggle id="allow-on-all-sites" - checked="[[data.runOnAllUrls.isActive]]" - on-change="onAllowOnAllSitesChange_"></cr-toggle> - </div> + </extensions-toggle-row> </template> <template is="dom-if" if="[[data.errorCollection.isEnabled]]"> - <div class="control-line"> + <extensions-toggle-row id="collect-errors" + checked="[[data.errorCollection.isActive]]" + on-change="onCollectErrorsChange_"> <span>$i18n{itemCollectErrors}</span> - <cr-toggle id="collect-errors" - checked="[[data.errorCollection.isActive]]" - on-change="onCollectErrorsChange_"></cr-toggle> - </div> + </extensions-toggle-row> </template> </div> </template> - <div class="section" + <div class="section control-line actionable" id="extensions-options" + on-tap="onOptionsTap_" 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> + <span id="optionLabel">$i18n{itemOptions}</span> + <button class="icon-external" is="paper-icon-button-light" + aria-labelledby="optionLabel" actionable></button> </div> - <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 class="hr" hidden="[[!data.manifestHomePageUrl.length]]" + is="cr-link-row" icon-class="icon-external" id="developerWebsite" + label="$i18n{developerWebsite}" on-tap="onDeveloperWebSiteTap_"> + </button> + <button class="hr" hidden="[[!data.webStoreUrl.length]]" + is="cr-link-row" icon-class="icon-external" id="viewInStore" + label="$i18n{viewInStore}" on-tap="onViewInStoreTap_"> </button> <div class="section block"> <div class="section-title">$i18n{itemSource}</div> - <div class="section-content"> + <div id="source" class="section-content"> [[computeSourceString_(data.*)]] </div> + <div id="load-path" class="section-content" + hidden$="[[!data.prettifiedPath]]"> + <span>$i18n{itemExtensionPath}</span> + <a is="action-link" on-tap="onLoadPathTap_"> + [[data.prettifiedPath]] + </a> + </div> </div> + <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> </div> </template> diff --git a/chromium/chrome/browser/resources/md_extensions/detail_view.js b/chromium/chrome/browser/resources/md_extensions/detail_view.js index 9f7ab51e2db..0eb723e9679 100644 --- a/chromium/chrome/browser/resources/md_extensions/detail_view.js +++ b/chromium/chrome/browser/resources/md_extensions/detail_view.js @@ -8,7 +8,11 @@ cr.define('extensions', function() { const DetailView = Polymer({ is: 'extensions-detail-view', - behaviors: [I18nBehavior, CrContainerShadowBehavior], + behaviors: [ + I18nBehavior, + CrContainerShadowBehavior, + extensions.ItemBehavior, + ], properties: { /** @@ -17,6 +21,9 @@ cr.define('extensions', function() { */ data: Object, + /** @private */ + size_: String, + /** @type {!extensions.ItemDelegate} */ delegate: Object, @@ -24,10 +31,33 @@ cr.define('extensions', function() { inDevMode: Boolean, }, + observers: [ + 'onItemIdChanged_(data.id, delegate)', + ], + + /** @private */ + onItemIdChanged_: function() { + // Clear the size, since this view is reused, such that no obsolete size + // is displayed.: + this.size_ = ''; + this.delegate.getExtensionSize(this.data.id).then(size => { + this.size_ = size; + }); + }, + + /** + * @param {string} description + * @param {string} fallback + * @return {string} + * @private + */ + getDescription_: function(description, fallback) { + return description || fallback; + }, + /** @private */ onCloseButtonTap_: function() { - extensions.navigation.navigateTo( - {page: Page.LIST, type: extensions.getItemListType(this.data)}); + extensions.navigation.navigateTo({page: Page.LIST}); }, /** @@ -66,18 +96,18 @@ cr.define('extensions', function() { * @return {boolean} * @private */ - hasPermissions_: function() { - return this.data.permissions.length > 0; + hasWarnings_: function() { + return this.data.disableReasons.corruptInstall || + this.data.disableReasons.suspiciousInstall || + this.data.disableReasons.updateRequired || !!this.data.blacklistText; }, /** - * @return {boolean} + * @return {string} * @private */ - hasWarnings_: function() { - return this.data.disableReasons.corruptInstall || - this.data.disableReasons.suspiciousInstall || - this.data.disableReasons.updateRequired || !!this.data.blacklistText; + computeEnabledStyle_: function() { + return this.isEnabled_() ? 'enabled-text' : ''; }, /** @@ -102,18 +132,6 @@ cr.define('extensions', function() { * @return {boolean} * @private */ - shouldShowHomepageButton_: function() { - // Note: we ignore |data.homePage.specified| - we use an extension's - // webstore entry as a homepage if the extension didn't explicitly specify - // a homepage. (|url| can still be unset in the case of unpacked - // extensions.) - return this.data.homePage.url.length > 0; - }, - - /** - * @return {boolean} - * @private - */ shouldShowOptionsLink_: function() { return !!this.data.optionsPage; }, @@ -144,7 +162,7 @@ cr.define('extensions', function() { /** @private */ onOptionsTap_: function() { - this.delegate.showItemOptionsPage(this.data.id); + this.delegate.showItemOptionsPage(this.data); }, /** @private */ @@ -158,6 +176,11 @@ cr.define('extensions', function() { }, /** @private */ + onLoadPathTap_: function() { + this.delegate.showInFolder(this.data.id); + }, + + /** @private */ onAllowIncognitoChange_: function() { this.delegate.setItemAllowedIncognito( this.data.id, this.$$('#allow-incognito').checked); @@ -181,18 +204,32 @@ cr.define('extensions', function() { this.data.id, this.$$('#collect-errors').checked); }, + /** @private */ + onDeveloperWebSiteTap_: function() { + this.delegate.openUrl(this.data.manifestHomePageUrl); + }, + + /** @private */ + onViewInStoreTap_: function() { + this.delegate.openUrl(this.data.webStoreUrl); + }, + /** * @param {!chrome.developerPrivate.DependentExtension} item + * @return {string} * @private */ computeDependentEntry_: function(item) { return loadTimeData.getStringF('itemDependentEntry', item.name, item.id); }, - /** @private */ + /** + * @return {string} + * @private + */ computeSourceString_: function() { - return extensions.getItemSourceString( - extensions.getItemSource(this.data)); + return this.data.locationText || + extensions.getItemSourceString(extensions.getItemSource(this.data)); }, /** diff --git a/chromium/chrome/browser/resources/md_extensions/drag_and_drop_handler.js b/chromium/chrome/browser/resources/md_extensions/drag_and_drop_handler.js index f79bb43cde1..862c3a436b5 100644 --- a/chromium/chrome/browser/resources/md_extensions/drag_and_drop_handler.js +++ b/chromium/chrome/browser/resources/md_extensions/drag_and_drop_handler.js @@ -7,21 +7,27 @@ cr.define('extensions', function() { /** * @param {boolean} dragEnabled + * @param {boolean} isMdExtensions * @param {!EventTarget} target * @constructor * @implements cr.ui.DragWrapperDelegate */ - function DragAndDropHandler(dragEnabled, target) { + function DragAndDropHandler(dragEnabled, isMdExtensions, target) { this.dragEnabled = dragEnabled; + + // Behavior is different for dropped directories between MD and non-MD + // extensions pages. + // TODO(devlin): Delete the non-MD codepath and remove this variable when + // MD extensions launches. + /** @private {boolean} */ + this.isMdExtensions_ = isMdExtensions; + /** @private {!EventTarget} */ this.eventTarget_ = target; } - // TODO(devlin): Un-chrome.send-ify this implementation. + // TODO(devlin): Finish un-chrome.send-ifying this implementation. DragAndDropHandler.prototype = { - /** @type {boolean} */ - dragEnabled: false, - /** @override */ shouldAcceptDrag: function(e) { // External Extension installation can be disabled globally, e.g. while a @@ -40,6 +46,9 @@ cr.define('extensions', function() { /** @override */ doDragEnter: function() { chrome.send('startDrag'); + if (this.isMdExtensions_) + chrome.developerPrivate.notifyDragInstallInProgress(); + this.eventTarget_.dispatchEvent( new CustomEvent('extension-drag-started')); }, @@ -61,27 +70,56 @@ cr.define('extensions', function() { if (e.dataTransfer.files.length != 1) return; - let toSend = ''; + let handled = false; + // Files lack a check if they're a directory, but we can find out through // its item entry. - 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'; - break; - } - } - // Only process files that look like extensions. Other files should - // navigate the browser normally. - if (!toSend && - /\.(crx|user\.js|zip)$/i.test(e.dataTransfer.files[0].name)) { - toSend = 'installDroppedFile'; + let item = e.dataTransfer.items[0]; + if (item.kind === 'file' && item.webkitGetAsEntry().isDirectory) { + handled = true; + this.handleDirectoryDrop_(); + } else if (/\.(crx|user\.js|zip)$/i.test(e.dataTransfer.files[0].name)) { + // Only process files that look like extensions. Other files should + // navigate the browser normally. + handled = true; + this.handleFileDrop_(); } - if (toSend) { + if (handled) e.preventDefault(); - chrome.send(toSend); + }, + + /** + * Handles a dropped file. + * @private + */ + handleFileDrop_: function() { + // Packaged files always go through chrome.send (for now). + chrome.send('installDroppedFile'); + }, + + /** + * Handles a dropped directory. + * @private + */ + handleDirectoryDrop_: function() { + // Dropped directories either go through developerPrivate or chrome.send + // depending on if this is the MD page. + if (!this.isMdExtensions_) { + chrome.send('installDroppedDirectory'); + return; } + + // TODO(devlin): Update this to use extensions.Service when it's not + // shared between the MD and non-MD pages. + chrome.developerPrivate.loadUnpacked( + {failQuietly: true, populateError: true, useDraggedPath: true}, + (loadError) => { + if (loadError) { + this.eventTarget_.dispatchEvent(new CustomEvent( + 'drag-and-drop-load-error', {detail: loadError})); + } + }); }, /** @private */ diff --git a/chromium/chrome/browser/resources/md_extensions/drop_overlay.html b/chromium/chrome/browser/resources/md_extensions/drop_overlay.html index 7c6c510578f..168ffe7a9df 100644 --- a/chromium/chrome/browser/resources/md_extensions/drop_overlay.html +++ b/chromium/chrome/browser/resources/md_extensions/drop_overlay.html @@ -12,11 +12,11 @@ <style include="cr-hidden-style"> :host { align-items: center; - background-color: white; + background-color: #f1f1f1; display: flex; height: 100%; justify-content: center; - opacity: 0.5; + opacity: 0.9; position: absolute; width: 100%; z-index: 10; @@ -29,14 +29,21 @@ } iron-icon { - color: var(--paper-teal-a400); + color: var(--paper-grey-600); height: 64px; + margin-bottom: 16px; width: 64px; } + + #text { + color: #6e6e6e; + font-size: 123.1%; + font-weight: 500; + } </style> <div id="container"> <iron-icon icon="extension"></iron-icon> - <div>$i18n{dropToInstall}</div> + <div id="text">$i18n{dropToInstall}</div> </div> </template> <script src="drop_overlay.js"></script> diff --git a/chromium/chrome/browser/resources/md_extensions/drop_overlay.js b/chromium/chrome/browser/resources/md_extensions/drop_overlay.js index feeddba5520..a1071ef6ca5 100644 --- a/chromium/chrome/browser/resources/md_extensions/drop_overlay.js +++ b/chromium/chrome/browser/resources/md_extensions/drop_overlay.js @@ -7,17 +7,22 @@ Polymer({ is: 'extensions-drop-overlay', + + /** @override */ created: function() { this.hidden = true; const dragTarget = document.documentElement; this.dragWrapperHandler_ = - new extensions.DragAndDropHandler(true, dragTarget); + new extensions.DragAndDropHandler(true, true, dragTarget); dragTarget.addEventListener('extension-drag-started', () => { this.hidden = false; }); dragTarget.addEventListener('extension-drag-ended', () => { this.hidden = true; }); + dragTarget.addEventListener('drag-and-drop-load-error', (e) => { + this.fire('load-error', e.detail); + }); this.dragWrapper_ = new cr.ui.DragWrapper(dragTarget, this.dragWrapperHandler_); }, diff --git a/chromium/chrome/browser/resources/md_extensions/error_page.html b/chromium/chrome/browser/resources/md_extensions/error_page.html index 0ddb9486ee4..d3fbc22f893 100644 --- a/chromium/chrome/browser/resources/md_extensions/error_page.html +++ b/chromium/chrome/browser/resources/md_extensions/error_page.html @@ -1,17 +1,17 @@ <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/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/html/cr/ui/focus_outline_manager.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://resources/polymer/v1_0/paper-styles/shadow.html"> <link rel="import" href="code_section.html"> <link rel="import" href="item_util.html"> <link rel="import" href="navigation_helper.html"> @@ -29,7 +29,24 @@ overflow: overlay; } + iron-icon { + --iron-icon-fill-color: var(--paper-grey-500); + @apply(--cr-icon-height-width); + } + + iron-icon[icon=warning] { + --iron-icon-fill-color: var(--paper-orange-500); + } + + iron-icon[icon=error] { + --iron-icon-fill-color: var(--paper-red-700); + } + + /* TODO(dpapad): There is a lot of duplicated styling between + * detail_view.html and error_page.html. Refactor such that no duplication + * happens.*/ #main { + @apply(--shadow-elevation-2dp); background-color: white; margin: auto; min-height: 100%; @@ -77,19 +94,6 @@ flex-grow: 1; } - .icon-severity-info { - /* TODO(devlin): 1x/2x versions? */ - content: url(error_severity_info.png); - } - - .icon-severity-warning { - content: url(error_severity_warning.png); - } - - .icon-severity-fatal { - content: url(error_severity_fatal.png); - } - #devtools-controls { padding: 0 var(--cr-section-padding); } @@ -130,26 +134,33 @@ /* TODO(scottchen): extract to shared location from settings. */ .separator { + --separator-gaps: 9px; -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; + flex-shrink: 0; + height: calc(var(--cr-section-min-height) - + var(--separator-gaps)); + } /* TODO(scottchen): extract to shared location from settings. */ .separator + button[is='paper-icon-button-light'] { -webkit-margin-start: var(--cr-icon-ripple-margin); } + + :host-context(.focus-outline-visible) .start:focus { + outline: -webkit-focus-ring-color auto 5px; + } + + .start:focus { + outline: none; + } </style> <div id="container"> <div id="main"> @@ -164,17 +175,17 @@ could-not-display-code="$i18n{noErrorsToShow}"> </extensions-code-section> <div id="errors-list"> - <template is="dom-repeat" items="[[calculateShownItems_(data.*)]]"> + <template is="dom-repeat" items="[[entries_]]"> <div class="item-container"> - <div class$="error-item [[computeErrorClass_(selectedError_, item)]]"> - <div class="start" on-tap="onErrorItemAction_" + <div class$="error-item + [[computeErrorClass_(item, selectedEntry_)]]"> + <div actionable class=" start" on-tap="onErrorItemAction_" on-keydown="onErrorItemAction_" tabindex="0"> - <img class$="[[computeErrorIconClass_(item)]]"> + <iron-icon icon$="[[computeErrorIcon_(item)]]"></iron-icon> <div class="error-message">[[item.message]]</div> - <cr-expand-button hidden="[[!computeIsRuntimeError_(item)]]" - expanded="[[isEqual_(selectedError_, item)]]" - tab-index="-1"> - </cr-expand-button> + <div class$="cr-icon [[iconName_(index, selectedEntry_)]]" + hidden="[[!computeIsRuntimeError_(item)]]"> + </div> </div> <div class="separator"></div> <button is="paper-icon-button-light" class="icon-delete-gray" @@ -183,7 +194,7 @@ </button> </div> <template is="dom-if" if="[[computeIsRuntimeError_(item)]]"> - <iron-collapse opened="[[isEqual_(selectedError_, item)]]"> + <iron-collapse opened="[[isOpened_(index, selectedEntry_)]]"> <div id="devtools-controls"> <div id="stack-trace-heading"> $i18n{stackTrace} @@ -192,7 +203,8 @@ <template is="dom-repeat" items="[[item.stackTrace]]"> <li on-tap="onStackFrameTap_" hidden="[[!shouldDisplayFrame_(item.url)]]" - class$="[[getStackFrameClass_(item, selectedStackFrame_)]]"> + class$="[[getStackFrameClass_(item, + selectedStackFrame_)]]"> [[getStackTraceLabel_(item)]] </li> </template> @@ -207,7 +219,7 @@ </template> </div> </template> - </iron-list> + </div> </div> </div> </div> diff --git a/chromium/chrome/browser/resources/md_extensions/error_page.js b/chromium/chrome/browser/resources/md_extensions/error_page.js index 113561d6018..b3dc9ca06ea 100644 --- a/chromium/chrome/browser/resources/md_extensions/error_page.js +++ b/chromium/chrome/browser/resources/md_extensions/error_page.js @@ -55,8 +55,14 @@ cr.define('extensions', function() { /** @type {!extensions.ErrorPageDelegate|undefined} */ delegate: Object, - /** @private {?(ManifestError|RuntimeError)} */ - selectedError_: Object, + /** @private {!Array<!(ManifestError|RuntimeError)>} */ + entries_: Array, + + /** + * Index into |entries_|. + * @private + */ + selectedEntry_: Number, /** @private {?chrome.developerPrivate.StackFrame}*/ selectedStackFrame_: { @@ -68,36 +74,34 @@ cr.define('extensions', function() { }, observers: [ - 'observeDataChanges_(data)', - 'onSelectedErrorChanged_(selectedError_)', + 'observeDataChanges_(data.*)', + 'onSelectedErrorChanged_(selectedEntry_)', ], + /** @override */ + ready: function() { + cr.ui.FocusOutlineManager.forDocument(document); + }, + + /** @return {!ManifestError|!RuntimeError} */ + getSelectedError: function() { + return this.entries_[this.selectedEntry_]; + }, + /** * Watches for changes to |data| in order to fetch the corresponding * file source. * @private */ observeDataChanges_: function() { - assert(this.data); - this.selectedError_ = - this.data.manifestErrors[0] || this.data.runtimeErrors[0] || null; - }, - - /** - * @return {!Array<!(ManifestError|RuntimeError)>} - * @private - */ - calculateShownItems_: function() { - return this.data.manifestErrors.concat(this.data.runtimeErrors); + const errors = this.data.manifestErrors.concat(this.data.runtimeErrors); + this.entries_ = errors; + this.selectedEntry_ = this.entries_.length ? 0 : -1; }, /** @private */ onCloseButtonTap_: function() { - extensions.navigation.navigateTo({ - page: Page.LIST, - type: extensions.getItemListType( - /** @type {!chrome.developerPrivate.ExtensionInfo} */ (this.data)) - }); + extensions.navigation.navigateTo({page: Page.LIST}); }, /** @@ -105,20 +109,20 @@ cr.define('extensions', function() { * @return {string} * @private */ - computeErrorIconClass_: function(error) { + computeErrorIcon_: function(error) { if (error.type == chrome.developerPrivate.ErrorType.RUNTIME) { switch (error.severity) { case chrome.developerPrivate.ErrorLevel.LOG: - return 'icon-severity-info'; + return 'info'; case chrome.developerPrivate.ErrorLevel.WARN: - return 'icon-severity-warning'; + return 'warning'; case chrome.developerPrivate.ErrorLevel.ERROR: - return 'icon-severity-fatal'; + return 'error'; } assertNotReached(); } assert(error.type == chrome.developerPrivate.ErrorType.MANIFEST); - return 'icon-severity-warning'; + return 'warning'; }, /** @@ -139,12 +143,12 @@ cr.define('extensions', function() { * @private */ onSelectedErrorChanged_: function() { - if (!this.selectedError_) { + if (this.selectedEntry_ < 0) { this.$['code-section'].code = null; return; } - const error = this.selectedError_; + const error = this.getSelectedError(); const args = { extensionId: error.extensionId, message: error.message, @@ -188,8 +192,8 @@ cr.define('extensions', function() { * @private */ getStackTraceLabel_: function(frame) { - let description = getRelativeUrl(frame.url, this.selectedError_) + ':' + - frame.lineNumber; + let description = getRelativeUrl(frame.url, this.getSelectedError()) + + ':' + frame.lineNumber; if (frame.functionName) { const functionName = frame.functionName == '(anonymous function)' ? @@ -232,15 +236,16 @@ cr.define('extensions', function() { * @private */ onStackFrameTap_: function(e) { - const frame = (/** @type {!{model:Object}} */ (e)).model.item; + const frame = /** @type {!{model:Object}} */ (e).model.item; this.selectedStackFrame_ = frame; + const selectedError = this.getSelectedError(); this.delegate .requestFileSource({ - extensionId: this.selectedError_.extensionId, - message: this.selectedError_.message, - pathSuffix: getRelativeUrl(frame.url, this.selectedError_), + extensionId: selectedError.extensionId, + message: selectedError.message, + pathSuffix: getRelativeUrl(frame.url, selectedError), lineNumber: frame.lineNumber, }) .then(code => { @@ -250,15 +255,14 @@ cr.define('extensions', function() { /** @private */ onDevToolButtonTap_: function() { + const selectedError = this.getSelectedError(); // This guarantees renderProcessId and renderViewId. - assert( - this.selectedError_.type == - chrome.developerPrivate.ErrorType.RUNTIME); + assert(selectedError.type == chrome.developerPrivate.ErrorType.RUNTIME); assert(this.selectedStackFrame_); this.delegate.openDevTools({ - renderProcessId: this.selectedError_.renderProcessId, - renderViewId: this.selectedError_.renderViewId, + renderProcessId: selectedError.renderProcessId, + renderViewId: selectedError.renderViewId, url: this.selectedStackFrame_.url, lineNumber: this.selectedStackFrame_.lineNumber || 0, columnNumber: this.selectedStackFrame_.columnNumber || 0, @@ -268,43 +272,40 @@ cr.define('extensions', function() { /** * Computes the class name for the error item depending on whether its * the currently selected error. - * @param {!RuntimeError|!ManifestError} selectedError - * @param {!RuntimeError|!ManifestError} error + * @param {number} index * @return {string} * @private */ - computeErrorClass_: function(selectedError, error) { - return selectedError == error ? 'selected' : ''; + computeErrorClass_: function(index) { + return index == this.selectedEntry_ ? 'selected' : ''; }, - /** - * @param {!RuntimeError|!ManifestError} selected - * @param {!RuntimeError|!ManifestError} current - * @return {boolean} - * @private - */ - isEqual_: function(selected, current) { - return selected == current; + /** @private */ + iconName_: function(index) { + return index == this.selectedEntry_ ? 'icon-expand-less' : + 'icon-expand-more'; }, /** - * Causes the given error to become the currently-selected error. - * @param {!RuntimeError|!ManifestError} error + * Determine if the iron-collapse should be opened (expanded). + * @param {number} index + * @return {boolean} * @private */ - selectError_: function(error) { - this.selectedError_ = error; + isOpened_: function(index) { + return index == this.selectedEntry_; }, /** - * @param {!{model: !{item: (!RuntimeError|!ManifestError)}}} e + * @param {!{model: !{index: number}}} e * @private */ onErrorItemAction_: function(e) { if (e.type == 'keydown' && !((e.code == 'Space' || e.code == 'Enter'))) return; - this.selectError_(e.model.item); + this.selectedEntry_ = + this.selectedEntry_ == e.model.index ? -1 : e.model.index; }, }); diff --git a/chromium/chrome/browser/resources/md_extensions/error_severity_fatal.png b/chromium/chrome/browser/resources/md_extensions/error_severity_fatal.png Binary files differdeleted file mode 100644 index b53dc2cf74b..00000000000 --- a/chromium/chrome/browser/resources/md_extensions/error_severity_fatal.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/md_extensions/error_severity_info.png b/chromium/chrome/browser/resources/md_extensions/error_severity_info.png Binary files differdeleted file mode 100644 index 1c32c6eb83f..00000000000 --- a/chromium/chrome/browser/resources/md_extensions/error_severity_info.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/md_extensions/error_severity_warning.png b/chromium/chrome/browser/resources/md_extensions/error_severity_warning.png Binary files differdeleted file mode 100644 index d2e3434a595..00000000000 --- a/chromium/chrome/browser/resources/md_extensions/error_severity_warning.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/md_extensions/extensions.html b/chromium/chrome/browser/resources/md_extensions/extensions.html index 1428eb4e94f..97ddab4934b 100644 --- a/chromium/chrome/browser/resources/md_extensions/extensions.html +++ b/chromium/chrome/browser/resources/md_extensions/extensions.html @@ -1,17 +1,27 @@ <!doctype html> -<html dir="$i18n{textdirection}" lang="$i18n{language}"> +<html dir="$i18n{textdirection}" lang="$i18n{language}" + class="loading $i18n{loadTimeClasses}"> <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="manager.html"> - <link rel="stylesheet" href="chrome://resources/css/md_colors.css"> <style> html { - background: var(--md-background-color); + /* --md-background-color in disguise. Not using the var for increased + * performance. */ + background-color: #f1f1f1; + } + + .loading { + /* --google-blue-700 in disguise. Replaced when manager.html loads. */ + border-top: 56px solid rgb(51, 103, 214); + } + + /* Note: .in-dev-mode is applied by i18n{loadTimeClasses}. */ + .loading.in-dev-mode { + border-top-width: 96px; } html, @@ -27,7 +37,6 @@ <body> <extensions-manager></extensions-manager> <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> - <link rel="import" href="service.html"> - <script src="extensions.js"></script> + <link rel="import" href="manager.html"> </body> </html> diff --git a/chromium/chrome/browser/resources/md_extensions/extensions.js b/chromium/chrome/browser/resources/md_extensions/extensions.js deleted file mode 100644 index a9a62659892..00000000000 --- a/chromium/chrome/browser/resources/md_extensions/extensions.js +++ /dev/null @@ -1,12 +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. - -(function() { -'use strict'; -const manager = /** @type {extensions.Manager} */ ( - document.querySelector('extensions-manager')); -manager.readyPromiseResolver.promise.then(function() { - extensions.Service.getInstance().managerReady(manager); -}); -})(); diff --git a/chromium/chrome/browser/resources/md_extensions/extensions_resources.grd b/chromium/chrome/browser/resources/md_extensions/extensions_resources.grd index c8d179899b3..7ae0088239c 100644 --- a/chromium/chrome/browser/resources/md_extensions/extensions_resources.grd +++ b/chromium/chrome/browser/resources/md_extensions/extensions_resources.grd @@ -12,14 +12,17 @@ </outputs> <release seq="1"> <structures> + <structure name="IDR_MD_EXTENSIONS_ITEM_BEHAVIOR_HTML" + file="item_behavior.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_ITEM_BEHAVIOR_JS" + file="item_behavior.js" + type="chrome_html" /> <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" /> @@ -84,6 +87,12 @@ <structure name="IDR_MD_EXTENSIONS_ICONS_HTML" file="icons.html" type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_INSTALL_WARNINGS_DIALOG_HTML" + file="install_warnings_dialog.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_INSTALL_WARNINGS_DIALOG_JS" + file="install_warnings_dialog.js" + type="chrome_html" /> <structure name="IDR_MD_EXTENSIONS_ITEM_HTML" file="item.html" type="chrome_html" /> @@ -156,6 +165,12 @@ <structure name="IDR_MD_EXTENSIONS_SIDEBAR_JS" file="sidebar.js" type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_TOGGLE_ROW_HTML" + file="toggle_row.html" + type="chrome_html" /> + <structure name="IDR_MD_EXTENSIONS_TOGGLE_ROW_JS" + file="toggle_row.js" + type="chrome_html" /> <structure name="IDR_MD_EXTENSIONS_TOOLBAR_HTML" file="toolbar.html" type="chrome_html" diff --git a/chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.html b/chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.html new file mode 100644 index 00000000000..87f807d0616 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.html @@ -0,0 +1,40 @@ +<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/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/polymer/v1_0/paper-styles/color.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="code_section.html"> + +<dom-module id="extensions-install-warnings-dialog"> + <template> + <style include="cr-shared-style paper-button-style"> + div[slot='body'] ul { + -webkit-padding-end: 10px; + background-color: var(--paper-pink-50); + margin: 0; + padding-bottom: 10px; + padding-top: 10px; + } + </style> + <dialog is="cr-dialog" id="dialog" close-text="$i18n{close}"> + <div slot="title">$i18n{installWarnings}</div> + <div slot="body"> + <ul> + <template is="dom-repeat" items="[[installWarnings]]"> + <li>[[item]]</li> + </template> + </ul> + </div> + <div slot="button-container"> + <paper-button class="action-button" on-tap="onOkTap_"> + $i18n{ok} + </paper-button> + </div> + </dialog> + </template> + <script src="install_warnings_dialog.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.js b/chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.js new file mode 100644 index 00000000000..5417428c937 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.js @@ -0,0 +1,28 @@ +// 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 InstallWarningsDialog = Polymer({ + is: 'extensions-install-warnings-dialog', + + properties: { + /** @type {!Array<string>} */ + installWarnings: Array, + }, + + /** @override */ + attached: function() { + this.$.dialog.showModal(); + }, + + /** @private */ + onOkTap_: function() { + this.$.dialog.close(); + }, + }); + + return {InstallWarningsDialog: InstallWarningsDialog}; +}); diff --git a/chromium/chrome/browser/resources/md_extensions/item.html b/chromium/chrome/browser/resources/md_extensions/item.html index 2f35703d131..c9c013e5b6b 100644 --- a/chromium/chrome/browser/resources/md_extensions/item.html +++ b/chromium/chrome/browser/resources/md_extensions/item.html @@ -4,11 +4,13 @@ <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/shared_vars_css.html"> <link rel="import" href="chrome://resources/html/action_link.html"> <link rel="import" href="chrome://resources/html/action_link_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="item_behavior.html"> <link rel="import" href="icons.html"> <link rel="import" href="item_util.html"> <link rel="import" href="strings.html"> @@ -17,16 +19,39 @@ <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-tooltip/paper-tooltip.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"> + .bounded-text, + .multiline-clippable-text, + .clippable-flex-text { + /** Ensure that the text does not overflow its container. */ + overflow: hidden; + text-overflow: ellipsis; + } + + .bounded-text, + .clippable-flex-text { + white-space: nowrap; + } + + .clipppable-flex-text { + /** + * These labels can be arbitrarily long. We want to ensure that these + * shrink, rather than the neighboring content. + */ + flex-shrink: 1; + } + #icon-wrapper { align-self: flex-start; display: flex; padding: 6px; + position: relative; } #icon { @@ -37,6 +62,7 @@ #card { @apply(--shadow-elevation-2dp); background: white; + border-radius: 2px; display: flex; flex-direction: column; height: 160px; @@ -49,16 +75,16 @@ #main { display: flex; - flex-grow: 1; + flex: 1; padding: 16px 20px 17px; - position: relative; } #content { -webkit-margin-start: 24px; display: flex; + flex: 1; flex-direction: column; - width: 100%; + overflow: hidden; } #name-and-version { @@ -70,9 +96,13 @@ -webkit-margin-end: 8px; } - #description, + #description { + flex: 1; + } + #warnings { - flex-grow: 1; + color: var(--google-red-700); + flex: 1; margin-bottom: 8px; } @@ -86,23 +116,42 @@ font-weight: 400; } + #extension-id { + flex-shrink: 0; + } + + #inspect-views { + display: flex; + white-space: nowrap; + } + + #inspect-views > span { + -webkit-margin-end: 4px; + } + #button-strip { - -webkit-padding-end: 4px; - -webkit-padding-start: var(--cr-section-padding); + /* Avoid ripple from overlapping container. */ + -webkit-margin-end: 20px; border-top: var(--cr-separator-line); box-sizing: border-box; + flex-shrink: 0; height: var(--cr-section-min-height); padding-bottom: 8px; padding-top: 8px; } #button-strip paper-button { - margin: 0 4px; + -webkit-margin-start: 8px; + } + + #learn-more-link { + color: var(--google-blue-700); + text-decoration: none; } #source-indicator { - -webkit-margin-start: 30px; - margin-top: 30px; + -webkit-margin-start: 24px; + margin-top: 24px; position: absolute; } @@ -124,30 +173,11 @@ width: 16px; } - #source-indicator-text { - -webkit-margin-start: 11px; /* To align with the center of the icon. */ - background: black; - border-radius: 2px; - color: white; - display: none; - margin-top: 8px; - max-width: 150px; - opacity: 0.6; - padding: 8px 12px; - 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; - } - - cr-toggle { - -webkit-margin-end: 8px; /* Avoid ripple from overlapping container. */ + paper-tooltip { + --paper-tooltip: { + @apply(--cr-tooltip); + min-width: 0; + }; } .action-button { @@ -162,68 +192,92 @@ -webkit-margin-end: 12px; } - #warnings { - color: var(--google-red-700); - } - #blacklisted-warning:empty { display: none; } </style> <div id="card" class$="[[computeClasses_(data.state, inDevMode)]]"> <div id="main"> + <div id="icon-wrapper"> + <img id="icon" src="[[data.iconUrl]]" + alt$="[[appOrExtension( + data.type, + '$i18nPolymer{appIcon}', + '$i18nPolymer{extensionIcon}')]]"> + <template is="dom-if" + if="[[computeSourceIndicatorIcon_(data.*)]]"> + <div id="source-indicator"> + <div class="source-icon-wrapper" role="img" + aria-label$="[[computeSourceIndicatorText_(data.*)]]"> + <iron-icon icon="[[computeSourceIndicatorIcon_(data.*)]]"> + </iron-icon> + </div> + </div> + </template> + </div> + <!-- This needs to be separate from the source-indicator since it can't + be contained inside of a position:relative parent element. --> <template is="dom-if" if="[[computeSourceIndicatorIcon_(data.*)]]"> - <div id="source-indicator"> - <div class="source-icon-wrapper"> - <iron-icon icon="[[computeSourceIndicatorIcon_(data.*)]]"> - </iron-icon> - </div> - <div id="source-indicator-text"> - [[computeSourceIndicatorText_(data.*)]] - </div> - </div> + <paper-tooltip id="source-indicator-text" for="source-indicator" + position="top" fit-to-visible-bounds> + [[computeSourceIndicatorText_(data.*)]] + </paper-tooltip> </template> - <div id="icon-wrapper"> - <img alt="" id="icon" src="[[data.iconUrl]]"> - </div> <div id="content"> - <div id="name-and-version" class="layout horizontal center"> - <div id="name">[[data.name]]</div> - <template is="dom-if" if="[[inDevMode]]"> - <span id="version">[[data.version]]</span> - </template> + <!--Note: We wrap inspect-views in a div so that the outer div + doesn't shrink (because it's not display: flex).--> + <div> + <div id="name-and-version" class="layout horizontal center"> + <div id="name" class="clippable-flex-text">[[data.name]]</div> + <span id="version" hidden$="[[!inDevMode]]"> + [[data.version]] + </span> + </div> </div> - <div id="description" hidden$="[[hasWarnings_(data.*)]]"> + <div id="description" class="multiline-clippable-text" + hidden$="[[hasWarnings_(data.*)]]"> [[data.description]] </div> - <div id="warnings" hidden$="[[!hasWarnings_(data.*)]]"> - <div id="suspicious-warning" - hidden$="[[!data.disableReasons.suspiciousInstall]]"> - $i18n{itemSuspiciousInstall} - </div> - <div id="corrupted-warning" - hidden$="[[!data.disableReasons.corruptInstall]]"> - $i18n{itemCorruptInstall} + <template is="dom-if" if="[[hasWarnings_(data.*)]]"> + <div id="warnings" > + <div id="suspicious-warning" + hidden$="[[!data.disableReasons.suspiciousInstall]]"> + $i18n{itemSuspiciousInstall} + <a target="_blank" id="learn-more-link" + href="$i18n{suspiciousInstallHelpUrl}"> + $i18n{learnMore} + </a> + </div> + <div id="corrupted-warning" + hidden$="[[!data.disableReasons.corruptInstall]]"> + $i18n{itemCorruptInstall} + </div> + <div id="blacklisted-warning"><!-- No whitespace + -->[[data.blacklistText]]<!-- so we can use :empty in css. + --></div> </div> - <div id="blacklisted-warning"><!-- No whitespace - -->[[data.blacklistText]]<!-- so we can use :empty in css. - --></div> - </div> + </template> <template is="dom-if" if="[[inDevMode]]"> - <div id="extension-id">[[data.id]]</div> + <div id="extension-id" class="bounded-text">[[data.id]]</div> <template is="dom-if" if="[[!computeInspectViewsHidden_(data.views)]]"> - <div id="inspect-views"> - <span>$i18n{itemInspectViews}</span> - <a is="action-link" on-tap="onInspectTap_"> - [[computeFirstInspectLabel_(data.views)]] - </a> - <a is="action-link" - hidden$="[[computeExtraViewsHidden_(data.views)]]" - on-tap="onExtraInspectTap_"> - [[computeExtraInspectLabel_(data.views)]] - </a> + <!--Note: We wrap inspect-views in a div so that the outer div + doesn't shrink (because it's not display: flex).--> + <div> + <div id="inspect-views"> + <span>$i18n{itemInspectViews}</span> + <a class="clippable-flex-text" is="action-link" + title="[[computeFirstInspectTitle_(data.views)]]" + on-tap="onInspectTap_"> + [[computeFirstInspectLabel_(data.views)]] + </a> + <a is="action-link" + hidden$="[[computeExtraViewsHidden_(data.views)]]" + on-tap="onExtraInspectTap_"> + [[computeExtraInspectLabel_(data.views)]] + </a> + </div> </div> </template> </template> @@ -238,27 +292,36 @@ hidden="[[isControlled_(data.controlledInfo)]]"> $i18n{itemRemove} </paper-button> - <paper-button id="errors-button" on-tap="onErrorsTap_" - hidden$="[[computeErrorsHidden_(data.*)]]"> - $i18n{itemErrors} - </paper-button> + <template is="dom-if" if="[[shouldShowErrorsButton_(data.*)]]"> + <paper-button id="errors-button" on-tap="onErrorsTap_"> + $i18n{itemErrors} + </paper-button> + </template> </div> <template is="dom-if" if="[[!computeDevReloadButtonHidden_(data.*)]]"> <button id="dev-reload-button" is="paper-icon-button-light" + aria-label="$i18n{itemReload}" class="icon-refresh no-overlap" on-tap="onReloadTap_"> </button> </template> - <paper-button id="repair-button" class="action-button" - on-tap="onRepairTap_" - hidden$="[[!data.disableReasons.corruptInstall]]"> - $i18n{itemRepair} - </paper-button> - <paper-button id="terminated-reload-button" on-tap="onReloadTap_" - class="action-button" - hidden$="[[!isTerminated_(data.state)]]"> - $i18n{itemReload} - </paper-button> + <template is="dom-if" if="[[data.disableReasons.corruptInstall]]"> + <paper-button id="repair-button" class="action-button" + on-tap="onRepairTap_"> + $i18n{itemRepair} + </paper-button> + </template> + <template is="dom-if" if="[[isTerminated_(data.state)]]"> + <paper-button id="terminated-reload-button" on-tap="onReloadTap_" + class="action-button"> + $i18n{itemReload} + </paper-button> + </template> <cr-toggle id="enable-toggle" class="action-button" + aria-label$="[[appOrExtension( + data.type, + '$i18nPolymer{appEnabled}', + '$i18nPolymer{extensionEnabled}')]]" + aria-describedby="name" checked="[[isEnabled_(data.state)]]" on-change="onEnableChange_" disabled="[[!isEnableToggleEnabled_(data.*)]]" hidden$="[[!showEnableToggle_(data.*)]]"> diff --git a/chromium/chrome/browser/resources/md_extensions/item.js b/chromium/chrome/browser/resources/md_extensions/item.js index f76a9fc0c95..f2fb9841cf1 100644 --- a/chromium/chrome/browser/resources/md_extensions/item.js +++ b/chromium/chrome/browser/resources/md_extensions/item.js @@ -44,20 +44,37 @@ cr.define('extensions', function() { */ inspectItemView(id, view) {} - /** @param {string} id */ + /** + * @param {string} url + */ + openUrl(url) {} + + /** + * @param {string} id + * @return {!Promise} + */ reloadItem(id) {} /** @param {string} id */ repairItem(id) {} + /** @param {!chrome.developerPrivate.ExtensionInfo} extension */ + showItemOptionsPage(extension) {} + /** @param {string} id */ - showItemOptionsPage(id) {} + showInFolder(id) {} + + /** + * @param {string} id + * @return {!Promise<string>} + */ + getExtensionSize(id) {} } const Item = Polymer({ is: 'extensions-item', - behaviors: [I18nBehavior], + behaviors: [I18nBehavior, extensions.ItemBehavior], properties: { // The item's delegate, or null. @@ -104,9 +121,17 @@ cr.define('extensions', function() { * @return {boolean} * @private */ - computeErrorsHidden_: function() { - return !this.data.manifestErrors.length && - !this.data.runtimeErrors.length; + shouldShowErrorsButton_: function() { + // When the error console is disabled (happens when + // --disable-error-console command line flag is used or when in the + // Stable/Beta channel), |installWarnings| is populated. + if (this.data.installWarnings && this.data.installWarnings.length > 0) + return true; + + // When error console is enabled |installedWarnings| is not populated. + // Instead |manifestErrors| and |runtimeErrors| are used. + return this.data.manifestErrors.length > 0 || + this.data.runtimeErrors.length > 0; }, /** @private */ @@ -122,6 +147,11 @@ cr.define('extensions', function() { /** @private */ onErrorsTap_: function() { + if (this.data.installWarnings && this.data.installWarnings.length > 0) { + this.fire('show-install-warnings', this.data.installWarnings); + return; + } + extensions.navigation.navigateTo( {page: Page.ERRORS, extensionId: this.data.id}); }, @@ -148,7 +178,9 @@ cr.define('extensions', function() { /** @private */ onReloadTap_: function() { - this.delegate.reloadItem(this.data.id); + this.delegate.reloadItem(this.data.id).catch(loadError => { + this.fire('load-error', loadError); + }); }, /** @private */ @@ -220,6 +252,9 @@ cr.define('extensions', function() { return 'communication:business'; case SourceType.SIDELOADED: return 'input'; + case SourceType.UNKNOWN: + // TODO(dpapad): Ask UX for a better icon for this case. + return 'input'; case SourceType.UNPACKED: return 'extensions-icons:unpacked'; case SourceType.WEBSTORE: @@ -233,6 +268,9 @@ cr.define('extensions', function() { * @private */ computeSourceIndicatorText_: function() { + if (this.data.locationText) + return this.data.locationText; + const sourceType = extensions.getItemSource(this.data); return sourceType == SourceType.WEBSTORE ? '' : @@ -251,18 +289,24 @@ cr.define('extensions', function() { * @return {string} * @private */ - computeFirstInspectLabel_: function() { + computeFirstInspectTitle_: function() { // Note: theoretically, this wouldn't be called without any inspectable // views (because it's in a dom-if="!computeInspectViewsHidden_()"). // However, due to the recycling behavior of iron list, it seems that // sometimes it can. Even when it is, the UI behaves properly, but we // need to handle the case gracefully. - if (this.data.views.length == 0) - return ''; - let label = extensions.computeInspectableViewLabel(this.data.views[0]); - if (this.data.views.length > 1) - label += ','; - return label; + return this.data.views.length > 0 ? + extensions.computeInspectableViewLabel(this.data.views[0]) : + ''; + }, + + /** + * @return {string} + * @private + */ + computeFirstInspectLabel_: function() { + let label = this.computeFirstInspectTitle_(); + return label && this.data.views.length > 1 ? label + ',' : label; }, /** diff --git a/chromium/chrome/browser/resources/md_extensions/item_behavior.html b/chromium/chrome/browser/resources/md_extensions/item_behavior.html new file mode 100644 index 00000000000..b0369e6dc6d --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/item_behavior.html @@ -0,0 +1 @@ +<script src="item_behavior.js"></script> diff --git a/chromium/chrome/browser/resources/md_extensions/item_behavior.js b/chromium/chrome/browser/resources/md_extensions/item_behavior.js new file mode 100644 index 00000000000..40241f5eb2d --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/item_behavior.js @@ -0,0 +1,30 @@ +// 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() { + /** @polymerBehavior */ + const ItemBehavior = { + /** + * @param {chrome.developerPrivate.ExtensionType} type + * @param {string} appLabel + * @param {string} extensionLabel + * @return {string} The app or extension label depending on |type|. + */ + appOrExtension: function(type, appLabel, extensionLabel) { + const ExtensionType = chrome.developerPrivate.ExtensionType; + switch (type) { + case ExtensionType.HOSTED_APP: + case ExtensionType.LEGACY_PACKAGED_APP: + case ExtensionType.PLATFORM_APP: + return appLabel; + case ExtensionType.EXTENSION: + case ExtensionType.SHARED_MODULE: + return extensionLabel; + } + assertNotReached('Item type is not App or Extension.'); + }, + }; + + return {ItemBehavior: ItemBehavior}; +}); diff --git a/chromium/chrome/browser/resources/md_extensions/item_list.html b/chromium/chrome/browser/resources/md_extensions/item_list.html index 13b0c0bc166..2ac11babaa0 100644 --- a/chromium/chrome/browser/resources/md_extensions/item_list.html +++ b/chromium/chrome/browser/resources/md_extensions/item_list.html @@ -3,19 +3,23 @@ <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/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html"> <link rel="import" href="item.html"> <dom-module id="extensions-item-list"> <template> <style include="cr-shared-style"> - #items-container, + .items-container, extensions-item { --extensions-card-width: 400px; } #container { + box-sizing: border-box; height: 100%; overflow: overlay; + padding: 24px 60px 64px; } .empty-list-message { @@ -26,7 +30,7 @@ text-align: center; } - #items-container { + .items-container { --grid-gutter: 12px; --max-columns: 3; display: grid; @@ -37,33 +41,62 @@ margin: auto; max-width: calc(var(--extensions-card-width) * var(--max-columns) + var(--grid-gutter) * var(--max-columns)); - padding: calc(var(--grid-gutter) / 2); } extensions-item { grid-column-start: auto; grid-row-start: auto; } + + #app-title { + @apply(--cr-section-text); + margin-bottom: 12px; + margin-top: 21px; + } </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> + hidden$="[[!shouldShowEmptyItemsMessage_( + apps.length, extensions.length)]]"> + <span on-tap="onNoExtensionsTap_">$i18nRaw{noExtensionsOrApps}</span> </div> <div id="no-search-results" class="empty-list-message" - hidden$="[[!shouldShowEmptySearchMessage_(shownItems_.length)]]"> + hidden$="[[!shouldShowEmptySearchMessage_( + shownAppsCount_, shownExtensionsCount_, apps, extensions)]]"> <span>$i18n{noSearchResults}</span> </div> - <div id="items-container"> - <template is="dom-repeat" items="[[shownItems_]]"> - <extensions-item data="[[item]]" delegate="[[delegate]]" - in-dev-mode="[[inDevMode]]"> + <div class="items-container" hidden="[[!shownExtensionsCount_]]"> + <!-- Render only a few items first, to improve initial render time, then + render the remaining items on a different frame. Value of 3 was chosen + by experimentation, and it is a good trade-off between initial render + time and total render time. --> + <template is="dom-repeat" items="[[extensions]]" initial-count="3" + filter="[[computedFilter_]]" + rendered-item-count="{{shownExtensionsCount_::dom-change}}" + notify-dom-change> + <extensions-item id="[[item.id]]" data="[[item]]" + delegate="[[delegate]]" in-dev-mode="[[inDevMode]]"> </extensions-item> </template> </div> + <div hidden="[[!shownAppsCount_]]"> + <!-- app-title needs to left-align with the grid content below, and + the easiest way to achieve this is to make it a grid as well. --> + <h2 id="app-title" class="items-container">$i18n{appsTitle}</h2> + <div class="items-container"> + <template is="dom-repeat" items="[[apps]]" initial-count="3" + filter="[[computedFilter_]]" + rendered-item-count="{{shownAppsCount_::dom-change}}" + notify-dom-change> + <extensions-item id="[[item.id]]" data="[[item]]" + delegate="[[delegate]]" in-dev-mode="[[inDevMode]]"> + </extensions-item> + </template> + </div> + </div> </div> </template> <script src="item_list.js"></script> diff --git a/chromium/chrome/browser/resources/md_extensions/item_list.js b/chromium/chrome/browser/resources/md_extensions/item_list.js index e6cd997e5ea..834f0cb4f4b 100644 --- a/chromium/chrome/browser/resources/md_extensions/item_list.js +++ b/chromium/chrome/browser/resources/md_extensions/item_list.js @@ -6,11 +6,14 @@ cr.define('extensions', function() { const ItemList = Polymer({ is: 'extensions-item-list', - behaviors: [CrContainerShadowBehavior], + behaviors: [CrContainerShadowBehavior, I18nBehavior], properties: { - /** @type {Array<!chrome.developerPrivate.ExtensionInfo>} */ - items: Array, + /** @type {!Array<!chrome.developerPrivate.ExtensionInfo>} */ + apps: Array, + + /** @type {!Array<!chrome.developerPrivate.ExtensionInfo>} */ + extensions: Array, /** @type {extensions.ItemDelegate} */ delegate: Object, @@ -22,37 +25,90 @@ cr.define('extensions', function() { isGuest: Boolean, - filter: String, + filter: { + type: String, + }, - /** @private {Array<!chrome.developerPrivate.ExtensionInfo>} */ - shownItems_: { - type: Array, - computed: 'computeShownItems_(items.*, filter)', - } + /** @private */ + computedFilter_: { + type: String, + computed: 'computeFilter_(filter)', + observer: 'announceSearchResults_', + }, + + /** @private */ + shownExtensionsCount_: { + type: Number, + value: 0, + }, + + /** @private */ + shownAppsCount_: { + type: Number, + value: 0, + }, + }, + + /** + * @param {string} id + * @return {?Element} + */ + getDetailsButton: function(id) { + return this.$$(`#${id} /deep/ #details-button`); }, /** - * Computes the list of items to be shown. - * @param {Object} changeRecord The changeRecord for |items|. - * @param {string} filter The updated filter string. - * @return {Array<!chrome.developerPrivate.ExtensionInfo>} + * @param {string} id + * @return {?Element} + */ + getErrorsButton: function(id) { + return this.$$(`#${id} /deep/ #errors-button`); + }, + + /** + * Computes the filter function to be used for determining which items + * should be shown. A |null| value indicates that everything should be + * shown. + * return {?Function} * @private */ - computeShownItems_: function(changeRecord, filter) { + computeFilter_: function() { const formattedFilter = this.filter.trim().toLowerCase(); - return this.items.filter( - item => item.name.toLowerCase().includes(formattedFilter)); + return formattedFilter ? + i => i.name.toLowerCase().includes(formattedFilter) : + null; }, /** @private */ shouldShowEmptyItemsMessage_: function() { - return !this.isGuest && this.items.length === 0; + return !this.isGuest && this.apps.length === 0 && + this.extensions.length === 0; }, /** @private */ shouldShowEmptySearchMessage_: function() { return !this.isGuest && !this.shouldShowEmptyItemsMessage_() && - this.shownItems_.length === 0; + this.shownAppsCount_ === 0 && this.shownExtensionsCount_ === 0; + }, + + /** @private */ + onNoExtensionsTap_: function(e) { + if (e.target.tagName == 'A') + chrome.metricsPrivate.recordUserAction('Options_GetMoreExtensions'); + }, + + /** @private */ + announceSearchResults_: function() { + if (this.computedFilter_) { + Polymer.IronA11yAnnouncer.requestAvailability(); + this.async(() => { // Async to allow list to update. + this.fire('iron-announce', { + text: this.shouldShowEmptySearchMessage_() ? + this.i18n('noSearchResults') : + this.i18n('searchResults', this.filter), + }); + }); + } }, }); diff --git a/chromium/chrome/browser/resources/md_extensions/item_util.js b/chromium/chrome/browser/resources/md_extensions/item_util.js index 5997a53b00f..aaefd16eb47 100644 --- a/chromium/chrome/browser/resources/md_extensions/item_util.js +++ b/chromium/chrome/browser/resources/md_extensions/item_util.js @@ -9,6 +9,7 @@ const SourceType = { POLICY: 'policy', SIDELOADED: 'sideloaded', UNPACKED: 'unpacked', + UNKNOWN: 'unknown', }; cr.define('extensions', function() { @@ -74,11 +75,19 @@ cr.define('extensions', function() { chrome.developerPrivate.ControllerType.POLICY) { return SourceType.POLICY; } - if (item.location == chrome.developerPrivate.Location.THIRD_PARTY) - return SourceType.SIDELOADED; - if (item.location == chrome.developerPrivate.Location.UNPACKED) - return SourceType.UNPACKED; - return SourceType.WEBSTORE; + + switch (item.location) { + case chrome.developerPrivate.Location.THIRD_PARTY: + return SourceType.SIDELOADED; + case chrome.developerPrivate.Location.UNPACKED: + return SourceType.UNPACKED; + case chrome.developerPrivate.Location.UNKNOWN: + return SourceType.UNKNOWN; + case chrome.developerPrivate.Location.FROM_STORE: + return SourceType.WEBSTORE; + } + + assertNotReached(item.location); } /** @@ -95,6 +104,10 @@ cr.define('extensions', function() { return loadTimeData.getString('itemSourceUnpacked'); case SourceType.WEBSTORE: return loadTimeData.getString('itemSourceWebstore'); + case SourceType.UNKNOWN: + // Nothing to return. Calling code should use + // chrome.developerPrivate.ExtensionInfo's |locationText| instead. + return ''; } assertNotReached(); } @@ -123,33 +136,10 @@ cr.define('extensions', function() { return label; } - /** - * Returns the list type that the item belongs to. - * @param {!chrome.developerPrivate.ExtensionInfo} item - * @return {extensions.ShowingType} - */ - function getItemListType(item) { - const ExtensionType = chrome.developerPrivate.ExtensionType; - switch (item.type) { - case ExtensionType.HOSTED_APP: - case ExtensionType.LEGACY_PACKAGED_APP: - case ExtensionType.PLATFORM_APP: - return extensions.ShowingType.APPS; - case ExtensionType.EXTENSION: - case ExtensionType.SHARED_MODULE: - return extensions.ShowingType.EXTENSIONS; - case ExtensionType.THEME: - assertNotReached('Don\'t send themes to the chrome://extensions page'); - break; - } - assertNotReached(); - } - return { isControlled: isControlled, isEnabled: isEnabled, userCanChangeEnablement: userCanChangeEnablement, - getItemListType: getItemListType, getItemSource: getItemSource, getItemSourceString: getItemSourceString, computeInspectableViewLabel: computeInspectableViewLabel diff --git a/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html b/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html index ecde827f4f7..854db3556e8 100644 --- a/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html +++ b/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html @@ -7,30 +7,37 @@ <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="item_behavior.html"> <link rel="import" href="shortcut_input.html"> <dom-module id="extensions-keyboard-shortcuts"> <template> <style include="md-select cr-shared-style"> :host { + --card-max-width: 928px; + --card-min-width: 600px; height: 100%; } - #container { - height: 100%; - overflow: overlay; - padding-top: 24px; + .shortcut-card { + margin: 0 auto 16px auto; + max-width: var(--card-max-width); + min-width: var(--card-min-width); + width: 90%; } .shortcut-card { - @apply(--cr-primary-text); - @apply(--shadow-elevation-2dp); + @apply --cr-primary-text; + @apply --shadow-elevation-2dp; background-color: white; - margin: 0 auto 16px auto; - max-width: 928px; - min-width: 600px; padding-bottom: 8px; - width: 90%; + } + + #container { + box-sizing: border-box; + height: 100%; + overflow: overlay; + padding-top: 24px; } .command-entry { @@ -64,7 +71,7 @@ display: flex; margin-bottom: 9px; padding: 16px var(--cr-section-padding); - @apply(--cr-title-text); + @apply --cr-title-text; } .icon { @@ -84,15 +91,19 @@ <template is="dom-repeat" items="[[calculateShownItems_(items.*)]]"> <div class="shortcut-card"> <div class="card-title"> - <img class="icon" src="[[item.iconUrl]]"> + <img class="icon" src="[[item.iconUrl]]" + alt$="[[appOrExtension( + item.type, + '$i18nPolymer{appIcon}', + '$i18nPolymer{extensionIcon}')]]"> <span>[[item.name]]</span> </div> <div class="card-controls"> <template is="dom-repeat" items="[[item.commands]]" as="command"> <div class="command-entry" command="[[command]]"> <span class="command-name">[[command.description]]</span> - <extensions-shortcut-input item="[[item.id]]" - shortcut="[[command.keybinding]]" + <extensions-shortcut-input delegate="[[delegate]]" + item="[[item.id]]" shortcut="[[command.keybinding]]" command-name="[[command.name]]"> </extensions-shortcut-input> <div class="md-select-wrapper"> diff --git a/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.js b/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.js index e75b5cecdab..1baad321ca1 100644 --- a/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.js +++ b/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.js @@ -9,7 +9,7 @@ cr.define('extensions', function() { const KeyboardShortcuts = Polymer({ is: 'extensions-keyboard-shortcuts', - behaviors: [CrContainerShadowBehavior], + behaviors: [CrContainerShadowBehavior, extensions.ItemBehavior], properties: { /** @type {Array<!chrome.developerPrivate.ExtensionInfo>} */ @@ -25,6 +25,15 @@ cr.define('extensions', function() { }, }, + listeners: { + 'view-enter-start': 'onViewEnter_', + }, + + /** @private */ + onViewEnter_: function() { + chrome.metricsPrivate.recordUserAction('Options_ExtensionCommands'); + }, + /** * @return {!Array<!chrome.developerPrivate.ExtensionInfo>} * @private diff --git a/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html b/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html index 51100a4ec56..2709a38694b 100644 --- a/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html +++ b/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html @@ -12,6 +12,7 @@ <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="item_behavior.html"> <link rel="import" href="kiosk_browser_proxy.html"> <dom-module id="extensions-kiosk-dialog"> @@ -82,7 +83,11 @@ <template is="dom-repeat" items="[[apps_]]"> <div class="list-item"> <div class="item-name"> - <img class="item-icon" src="[[item.iconURL]]"> + <img class="item-icon" src="[[item.iconURL]]" + alt$="[[appOrExtension( + data.type, + '$i18nPolymer{appIcon}', + '$i18nPolymer{extensionIcon}')]]"> [[item.name]] <span hidden="[[!item.autoLaunch]]"> $i18n{kioskAutoLaunch} diff --git a/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.js b/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.js index 254c2c14d81..66ada3fbebc 100644 --- a/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.js +++ b/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.js @@ -7,7 +7,7 @@ cr.define('extensions', function() { const KioskDialog = Polymer({ is: 'extensions-kiosk-dialog', - behaviors: [WebUIListenerBehavior], + behaviors: [WebUIListenerBehavior, extensions.ItemBehavior], properties: { /** @private {?string} */ addAppInput_: { diff --git a/chromium/chrome/browser/resources/md_extensions/load_error.html b/chromium/chrome/browser/resources/md_extensions/load_error.html index 0d0f1237a5c..d970402508e 100644 --- a/chromium/chrome/browser/resources/md_extensions/load_error.html +++ b/chromium/chrome/browser/resources/md_extensions/load_error.html @@ -6,6 +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://resources/polymer/v1_0/paper-spinner/paper-spinner.html"> <link rel="import" href="code_section.html"> <dom-module id="extensions-load-error"> @@ -38,10 +39,12 @@ </extensions-code-section> </div> <div slot="button-container"> + <paper-spinner active="[[retrying_]]"></paper-spinner> <paper-button class="cancel-button" on-tap="close"> $i18n{loadErrorCancel} </paper-button> - <paper-button class="action-button" on-tap="onRetryTap_"> + <paper-button class="action-button" disabled="[[retrying_]]" + on-tap="onRetryTap_"> $i18n{loadErrorRetry} </paper-button> </div> diff --git a/chromium/chrome/browser/resources/md_extensions/load_error.js b/chromium/chrome/browser/resources/md_extensions/load_error.js index 67391a189ff..8c22a6b8d17 100644 --- a/chromium/chrome/browser/resources/md_extensions/load_error.js +++ b/chromium/chrome/browser/resources/md_extensions/load_error.js @@ -10,6 +10,7 @@ cr.define('extensions', function() { /** * Attempts to load the previously-attempted unpacked extension. * @param {string} retryId + * @return {!Promise} */ retryLoadUnpacked(retryId) {} } @@ -22,6 +23,9 @@ cr.define('extensions', function() { /** @type {chrome.developerPrivate.LoadError} */ loadError: Object, + + /** @private */ + retrying_: Boolean, }, observers: [ @@ -38,8 +42,18 @@ cr.define('extensions', function() { /** @private */ onRetryTap_: function() { - this.delegate.retryLoadUnpacked(this.loadError.retryGuid); - this.close(); + this.retrying_ = true; + this.delegate.retryLoadUnpacked(this.loadError.retryGuid) + .then( + () => { + this.close(); + }, + loadError => { + this.loadError = + /** @type {chrome.developerPrivate.LoadError} */ ( + loadError); + this.retrying_ = false; + }); }, /** @private */ diff --git a/chromium/chrome/browser/resources/md_extensions/manager.html b/chromium/chrome/browser/resources/md_extensions/manager.html index c38263894b1..c4fcc5b96c5 100644 --- a/chromium/chrome/browser/resources/md_extensions/manager.html +++ b/chromium/chrome/browser/resources/md_extensions/manager.html @@ -1,15 +1,15 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_drawer/cr_drawer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.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/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/promise_resolver.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="install_warnings_dialog.html"> <link rel="import" href="item_list.html"> <link rel="import" href="item_util.html"> <link rel="import" href="keyboard_shortcuts.html"> @@ -65,42 +65,64 @@ </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> + <template is="dom-if" if="[[showDrawer_]]" restamp> + <dialog id="drawer" is="cr-drawer" heading="$i18n{toolbarTitle}" + align="$i18n{textdirection}" on-close="onDrawerClose_"> + <div class="drawer-content"> + <extensions-sidebar id="sidebar"></extensions-sidebar> + </div> + </dialog> + </template> <extensions-view-manager id="viewManager" role="main"> - <extensions-item-list id="items-list" items="[[itemsList_]]" + <extensions-item-list id="items-list" is-guest="[[isGuest_]]" delegate="[[delegate]]" in-dev-mode="[[inDevMode]]" filter="[[filter]]" hidden$="[[!didInitPage_]]" slot="view" - is-guest="[[isGuest_]]"> + apps="[[apps_]]" extensions="[[extensions_]]" + on-show-install-warnings="onShowInstallWarnings_"> </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> + <template id="details-view" is="cr-lazy-render"> + <extensions-detail-view delegate="[[delegate]]" slot="view" + in-dev-mode="[[inDevMode]]" data="[[detailViewItem_]]"> + </extensions-detail-view> + </template> + <template id="keyboard-shortcuts" is="cr-lazy-render"> + <extensions-keyboard-shortcuts delegate="[[delegate]]" slot="view" + items="[[extensions_]]"> + </extensions-keyboard-shortcuts> + </template> + <template id="error-page" is="cr-lazy-render"> + <extensions-error-page data="[[errorPageItem_]]" slot="view" + delegate="[[delegate]]" slot="view"> + </extensions-error-page> + </template> </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> + <template is="dom-if" if="[[showOptionsDialog_]]" restamp> + <extensions-options-dialog id="options-dialog" + on-close="onOptionsDialogClose_"> + </extensions-options-dialog> + </template> + <template is="dom-if" if="[[showPackDialog_]]" restamp> + <extensions-pack-dialog id="pack-dialog" delegate="[[delegate]]" + on-close="onPackDialogClose_"> + </extensions-pack-dialog> + </template> + <template is="dom-if" if="[[showLoadErrorDialog_]]" restamp> + <extensions-load-error id="load-error" delegate="[[delegate]]" + on-close="onLoadErrorDialogClose_"> + </extensions-load-error> + </template> <if expr="chromeos"> <template is="dom-if" if="[[showKioskDialog_]]" restamp> - <extensions-kiosk-dialog id="kiosk-dialog" - on-close="onKioskDialogClose_"> + <extensions-kiosk-dialog id="kiosk-dialog" on-close="onKioskDialogClose_"> </extensions-kiosk-dialog> </template> </if> + <template is="dom-if" if="[[showInstallWarningsDialog_]]" restamp> + <extensions-install-warnings-dialog + on-close="onInstallWarningsDialogClose_" + install-warnings="[[installWarnings_]]"> + </extensions-install-warnings-dialog> + </template> </template> <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 eb197cabeb3..8d434ead827 100644 --- a/chromium/chrome/browser/resources/md_extensions/manager.js +++ b/chromium/chrome/browser/resources/md_extensions/manager.js @@ -32,15 +32,15 @@ cr.define('extensions', function() { const Manager = Polymer({ is: 'extensions-manager', - behaviors: [I18nBehavior], - properties: { - /** @type {extensions.Toolbar} */ - toolbar: Object, - // This is not typed because it implements multiple interfaces, and is // passed to different elements as different types. - delegate: Object, + delegate: { + type: Object, + value: function() { + return extensions.Service.getInstance(); + }, + }, isGuest_: { type: Boolean, @@ -51,7 +51,7 @@ cr.define('extensions', function() { inDevMode: { type: Boolean, - value: false, + value: () => loadTimeData.getBoolean('inDevMode'), }, filter: { @@ -76,29 +76,11 @@ cr.define('extensions', function() { */ detailViewItem_: Object, - /** @type {!Array<!chrome.developerPrivate.ExtensionInfo>} */ - extensions: { - type: Array, - value: function() { - return []; - }, - }, + /** @private {!Array<!chrome.developerPrivate.ExtensionInfo>} */ + extensions_: Array, - /** @type {!Array<!chrome.developerPrivate.ExtensionInfo>} */ - apps: { - type: Array, - value: function() { - return []; - }, - }, - - /** @private {extensions.ShowingType} */ - listType_: Number, - - itemsList_: { - type: Array, - computed: 'computeList_(listType_)', - }, + /** @private {!Array<!chrome.developerPrivate.ExtensionInfo>} */ + apps_: Array, /** * Prevents page content from showing before data is first loaded. @@ -109,6 +91,24 @@ cr.define('extensions', function() { value: false, }, + /** @private */ + showDrawer_: Boolean, + + /** @private */ + showLoadErrorDialog_: Boolean, + + /** @private */ + showInstallWarningsDialog_: Boolean, + + /** @private {?Array<string>} */ + installWarnings_: Array, + + /** @private */ + showOptionsDialog_: Boolean, + + /** @private */ + showPackDialog_: Boolean, + // <if expr="chromeos"> /** @private */ kioskEnabled_: { @@ -124,30 +124,47 @@ cr.define('extensions', function() { // </if> }, + listeners: { + 'load-error': 'onLoadError_', + 'view-exit-finish': 'onViewExitFinish_', + }, + /** - * The current page being shown. Default to null, and initPage will figure + * The current page being shown. Default to null, and initPage_ will figure * out the initial page based on url. * @private {?PageState} */ currentPage_: null, /** - * The ID of the listner on |extensions.navigation|. Stored so that the + * The ID of the listener 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(); + if (loadTimeData.getBoolean('isGuest')) { + this.initPage_(); + return; + } + + let service = extensions.Service.getInstance(); + + let onProfileStateChanged = profileInfo => { + this.inDevMode = profileInfo.inDeveloperMode; + }; + service.getProfileStateChangedTarget().addListener(onProfileStateChanged); + service.getProfileConfiguration().then(onProfileStateChanged); + + service.getExtensionsInfo().then(extensionsAndApps => { + this.initExtensionsAndApps_(extensionsAndApps); + this.initPage_(); + + service.getItemStateChangedTarget().addListener( + this.onItemStateChanged_.bind(this)); + }); // <if expr="chromeos"> extensions.KioskBrowserProxyImpl.getInstance() @@ -160,6 +177,9 @@ cr.define('extensions', function() { /** @override */ attached: function() { + document.documentElement.classList.remove('loading'); + document.fonts.load('bold 12px Roboto'); + this.navigationListener_ = extensions.navigation.addListener(newPage => { this.changePage_(newPage); }); @@ -171,72 +191,93 @@ cr.define('extensions', function() { this.navigationListener_ = null; }, - get keyboardShortcuts() { - return this.$['keyboard-shortcuts']; - }, - - get packDialog() { - return this.$['pack-dialog']; - }, - - get loadError() { - return this.$['load-error']; - }, - - get optionsDialog() { - return this.$['options-dialog']; - }, - - get errorPage() { - return this.$['error-page']; - }, - /** * Initializes the page to reflect what's specified in the url so that if * the user visits chrome://extensions/?id=..., we land on the proper page. + * @private */ - initPage: function() { + initPage_: function() { this.didInitPage_ = true; this.changePage_(extensions.navigation.getCurrentPage()); }, /** + * @param {!chrome.developerPrivate.EventData} eventData + * @private + */ + onItemStateChanged_: function(eventData) { + const EventType = chrome.developerPrivate.EventType; + switch (eventData.event_type) { + case EventType.VIEW_REGISTERED: + case EventType.VIEW_UNREGISTERED: + case EventType.INSTALLED: + case EventType.LOADED: + case EventType.UNLOADED: + case EventType.ERROR_ADDED: + case EventType.ERRORS_REMOVED: + case EventType.PREFS_CHANGED: + // |extensionInfo| can be undefined in the case of an extension + // being unloaded right before uninstallation. There's nothing to do + // here. + if (!eventData.extensionInfo) + break; + + const listId = this.getListId_(eventData.extensionInfo); + const currentIndex = this[listId].findIndex( + item => item.id == eventData.extensionInfo.id); + + if (currentIndex >= 0) { + this.updateItem_(listId, currentIndex, eventData.extensionInfo); + } else { + this.addItem_(listId, eventData.extensionInfo); + } + break; + case EventType.UNINSTALLED: + this.removeItem_(eventData.item_id); + break; + default: + assertNotReached(); + } + }, + + /** * @param {!CustomEvent} event * @private */ onFilterChanged_: function(event) { + if (this.currentPage_.page !== Page.LIST) + extensions.navigation.navigateTo({page: Page.LIST}); this.filter = /** @type {string} */ (event.detail); }, /** @private */ onMenuButtonTap_: function() { - 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); + this.showDrawer_ = true; + this.async(() => { + this.$$('#drawer').openDrawer(); + }); }, /** - * @param {chrome.developerPrivate.ExtensionInfo} item + * @param {!chrome.developerPrivate.ExtensionInfo} item * @return {string} The ID of the list that the item belongs in. * @private */ getListId_: function(item) { - const type = extensions.getItemListType(item); - if (type == extensions.ShowingType.APPS) - return 'apps'; - else if (type == extensions.ShowingType.EXTENSIONS) - return 'extensions'; - + const ExtensionType = chrome.developerPrivate.ExtensionType; + switch (item.type) { + case ExtensionType.HOSTED_APP: + case ExtensionType.LEGACY_PACKAGED_APP: + case ExtensionType.PLATFORM_APP: + return 'apps_'; + case ExtensionType.EXTENSION: + case ExtensionType.SHARED_MODULE: + return 'extensions_'; + case ExtensionType.THEME: + assertNotReached( + 'Don\'t send themes to the chrome://extensions page'); + break; + } assertNotReached(); }, @@ -257,8 +298,27 @@ cr.define('extensions', function() { * @private */ getData_: function(id) { - return this.extensions[this.getIndexInList_('extensions', id)] || - this.apps[this.getIndexInList_('apps', id)]; + return this.extensions_[this.getIndexInList_('extensions_', id)] || + this.apps_[this.getIndexInList_('apps_', id)]; + }, + + /** + * Categorizes |extensionsAndApps| to apps and extensions and initializes + * those lists. + * @param {!Array<!chrome.developerPrivate.ExtensionInfo>} extensionsAndApps + * @private + */ + initExtensionsAndApps_: function(extensionsAndApps) { + extensionsAndApps.sort(compareExtensions); + let apps = []; + let extensions = []; + for (let i of extensionsAndApps) { + let list = this.getListId_(i) === 'apps_' ? apps : extensions; + list.push(i); + } + + this.apps_ = apps; + this.extensions_ = extensions; }, /** @@ -266,9 +326,9 @@ cr.define('extensions', function() { * into its sorted position in the relevant section. * @param {!chrome.developerPrivate.ExtensionInfo} item The extension * the new element is representing. + * @private */ - addItem: function(item) { - const listId = this.getListId_(item); + addItem_: function(listId, item) { // We should never try and add an existing item. assert(this.getIndexInList_(listId, item.id) == -1); let insertBeforeChild = this[listId].findIndex(function(listEl) { @@ -282,10 +342,9 @@ cr.define('extensions', function() { /** * @param {!chrome.developerPrivate.ExtensionInfo} item The data for the * item to update. + * @private */ - updateItem: function(item) { - const listId = this.getListId_(item); - const index = this.getIndexInList_(listId, item.id); + updateItem_: function(listId, index, item) { // We should never try and update a non-existent item. assert(index >= 0); this.set([listId, index], item); @@ -306,36 +365,43 @@ cr.define('extensions', function() { }, /** - * @param {!chrome.developerPrivate.ExtensionInfo} item The data for the - * item to remove. + * @param {string} itemId The id of item to remove. + * @private */ - removeItem: function(item) { - const listId = this.getListId_(item); - const index = this.getIndexInList_(listId, item.id); + removeItem_: function(itemId) { + // Search for the item to be deleted in |extensions_|. + let listId = 'extensions_'; + let index = this.getIndexInList_(listId, itemId); + if (index == -1) { + // If not in |extensions_| it must be in |apps_|. + listId = 'apps_'; + index = this.getIndexInList_(listId, itemId); + } + // We should never try and remove a non-existent item. assert(index >= 0); this.splice(listId, index, 1); + if ((this.currentPage_.page == Page.DETAILS || + this.currentPage_.page == Page.ERRORS) && + this.currentPage_.extensionId == itemId) { + // Leave the details page (the 'list' page is a fine choice). + extensions.navigation.replaceWith({page: Page.LIST}); + } }, /** - * @param {Page} page - * @return {!(extensions.KeyboardShortcuts | - * extensions.DetailView | - * extensions.ItemList)} + * @param {!CustomEvent} e * @private */ - getPage_: function(page) { - switch (page) { - case Page.LIST: - return this.$['items-list']; - case Page.DETAILS: - return this.$['details-view']; - case Page.SHORTCUTS: - return this.$['keyboard-shortcuts']; - case Page.ERRORS: - return this.$['error-page']; - } - assertNotReached(); + onLoadError_: function(e) { + const loadError = + /** @type {!chrome.developerPrivate.LoadError} */ (e.detail); + this.showLoadErrorDialog_ = true; + this.async(() => { + const dialog = this.$$('#load-error'); + dialog.loadError = loadError; + dialog.show(); + }); }, /** @@ -344,18 +410,29 @@ cr.define('extensions', function() { * @private */ changePage_: function(newPage) { - this.$.drawer.closeDrawer(); - if (this.optionsDialog && this.optionsDialog.open) - this.optionsDialog.close(); + const drawer = this.$$('#drawer'); + if (drawer && drawer.open) { + drawer.closeDrawer(); + this.showDrawer_ = false; + } + + const optionsDialog = this.$$('#options-dialog'); + if (optionsDialog && optionsDialog.open) { + optionsDialog.close(); + this.showOptionsDialog_ = false; + } const fromPage = this.currentPage_ ? this.currentPage_.page : null; const toPage = newPage.page; let data; - if (newPage.extensionId) - data = assert(this.getData_(newPage.extensionId)); - - if (newPage.hasOwnProperty('type')) - this.listType_ = /** @type {extensions.ShowingType} */ (newPage.type); + if (newPage.extensionId) { + data = this.getData_(newPage.extensionId); + if (!data) { + // Attempting to view an invalid (removed?) app or extension ID. + extensions.navigation.replaceWith({page: Page.LIST}); + return; + } + } if (toPage == Page.DETAILS) this.detailViewItem_ = assert(data); @@ -365,23 +442,84 @@ cr.define('extensions', function() { if (fromPage != toPage) { /** @type {extensions.ViewManager} */ (this.$.viewManager) .switchView(toPage); - } else { - /** @type {extensions.ViewManager} */ (this.$.viewManager) - .animateCurrentView('fade-in'); } if (newPage.subpage) { assert(newPage.subpage == Dialog.OPTIONS); assert(newPage.extensionId); - this.optionsDialog.show(data); + this.showOptionsDialog_ = true; + this.async(() => { + this.$$('#options-dialog').show(data); + }); } this.currentPage_ = newPage; }, /** @private */ + onDrawerClose_: function() { + this.showDrawer_ = false; + }, + + /** @private */ + onLoadErrorDialogClose_: function() { + this.showLoadErrorDialog_ = false; + }, + + /** @private */ + onOptionsDialogClose_: function() { + this.showOptionsDialog_ = false; + }, + + /** @private */ onPackTap_: function() { - this.$['pack-dialog'].show(); + this.showPackDialog_ = true; + this.async(() => { + this.$$('#pack-dialog').show(); + }); + }, + + /** @private */ + onPackDialogClose_: function() { + this.showPackDialog_ = false; + }, + + /** @private */ + onViewExitFinish_: function(e) { + const viewType = e.path[0].tagName; + if (viewType == 'EXTENSIONS-ITEM-LIST' || + viewType == 'EXTENSIONS-KEYBOARD-SHORTCUTS') { + return; + } + + const extensionId = e.path[0].data.id; + const list = this.$$('extensions-item-list'); + const button = viewType == 'EXTENSIONS-DETAIL-VIEW' ? + list.getDetailsButton(extensionId) : + list.getErrorsButton(extensionId); + + // The button will not exist, when returning from a details page + // because the corresponding extension/app was deleted. + if (button) + button.focus(); + }, + + /** + * @param {!CustomEvent} e + * @private + */ + onShowInstallWarnings_: function(e) { + // Leverage Polymer data bindings instead of just assigning the + // installWarnings on the dialog since the dialog hasn't been stamped + // in the DOM yet. + this.installWarnings_ = /** @type{!Array<string>} */ (e.detail); + this.showInstallWarningsDialog_ = true; + }, + + /** @private */ + onInstallWarningsDialogClose_: function() { + this.installWarnings_ = null; + this.showInstallWarningsDialog_ = false; }, // <if expr="chromeos"> @@ -394,24 +532,6 @@ cr.define('extensions', function() { this.showKioskDialog_ = false; }, // </if> - - /** - * @param {!extensions.ShowingType} listType - * @private - */ - computeList_: function(listType) { - // TODO(scottchen): the .slice is required to trigger the binding - // correctly, otherwise the list won't rerender. Should investigate - // the performance implication, or find better ways to trigger change. - switch (listType) { - case extensions.ShowingType.EXTENSIONS: - this.linkPaths('itemsList_', 'extensions'); - return this.extensions; - case extensions.ShowingType.APPS: - this.linkPaths('itemsList_', 'apps'); - return this.apps; - } - } }); return {Manager: Manager}; diff --git a/chromium/chrome/browser/resources/md_extensions/navigation_helper.js b/chromium/chrome/browser/resources/md_extensions/navigation_helper.js index 9437730ea02..e4efc690d4b 100644 --- a/chromium/chrome/browser/resources/md_extensions/navigation_helper.js +++ b/chromium/chrome/browser/resources/md_extensions/navigation_helper.js @@ -21,22 +21,25 @@ const Dialog = { OPTIONS: 'options', }; -/** @enum {number} */ -extensions.ShowingType = { - EXTENSIONS: 0, - APPS: 1, -}; - /** @typedef {{page: Page, extensionId: (string|undefined), - subpage: (!Dialog|undefined), - type: (!extensions.ShowingType|undefined)}} */ + subpage: (!Dialog|undefined)}} */ let PageState; cr.define('extensions', function() { 'use strict'; /** + * @param {!PageState} a + * @param {!PageState} b + * @return {boolean} Whether a and b are equal. + */ + function isPageStateEqual(a, b) { + return a.page == b.page && a.subpage == b.subpage && + a.extensionId == b.extensionId; + } + + /** * Regular expression that captures the leading slash, the content and the * trailing slash in three different groups. * @const {!RegExp} @@ -50,14 +53,7 @@ cr.define('extensions', function() { */ class NavigationHelper { constructor() { - // Redirect if route not supported. - let validPathnames = ['/']; - if (!loadTimeData.getBoolean('isGuest')) { - validPathnames.push('/shortcuts', '/apps'); - } - if (!validPathnames.includes(this.currentPath_)) { - window.history.replaceState(undefined, '', '/'); - } + this.processRoute_(); /** @private {number} */ this.nextListenerId_ = 1; @@ -65,6 +61,9 @@ cr.define('extensions', function() { /** @private {!Map<number, function(!PageState)>} */ this.listeners_ = new Map(); + /** @private {!PageState} */ + this.previousPage_; + window.addEventListener('popstate', () => { this.notifyRouteChanged_(this.getCurrentPage()); }); @@ -76,6 +75,24 @@ cr.define('extensions', function() { } /** + * If you're not a guest, going to /configureCommands and /shortcuts should + * land you on /shortcuts. These are the only two supported routes, so all + * other cases (guest or not) will redirect you to root path if not already + * on it. + * @private + */ + processRoute_() { + if (!loadTimeData.getBoolean('isGuest') && + (this.currentPath_ == '/configureCommands' || + this.currentPath_ == '/shortcuts')) { + window.history.replaceState( + undefined /* stateObject */, '', '/shortcuts'); + } else if (this.currentPath_ !== '/') { + window.history.replaceState(undefined /* stateObject */, '', '/'); + } + } + + /** * @return {!PageState} The page that should be displayed for the current * URL. */ @@ -94,10 +111,7 @@ cr.define('extensions', function() { if (this.currentPath_ == '/shortcuts') return {page: Page.SHORTCUTS}; - if (this.currentPath_ == '/apps') - return {page: Page.LIST, type: extensions.ShowingType.APPS}; - - return {page: Page.LIST, type: extensions.ShowingType.EXTENSIONS}; + return {page: Page.LIST}; } /** @@ -136,28 +150,36 @@ cr.define('extensions', function() { */ navigateTo(newPage) { let currentPage = this.getCurrentPage(); - if (currentPage && currentPage.page == newPage.page && - currentPage.type == newPage.type && - currentPage.subpage == newPage.subpage && - currentPage.extensionId == newPage.extensionId) { + if (currentPage && isPageStateEqual(currentPage, newPage)) { return; } - this.updateHistory(newPage); + this.updateHistory(newPage, false /* replaceState */); + this.notifyRouteChanged_(newPage); + } + + /** + * @param {!PageState} newPage the page to replace the current page with. + */ + replaceWith(newPage) { + this.updateHistory(newPage, true /* replaceState */); + if (this.previousPage_ && isPageStateEqual(this.previousPage_, newPage)) { + // Skip the duplicate history entry. + history.back(); + return; + } this.notifyRouteChanged_(newPage); } /** * Called when a page changes, and pushes state to history to reflect it. * @param {!PageState} entry + * @param {boolean} replaceState */ - updateHistory(entry) { + updateHistory(entry, replaceState) { let path; switch (entry.page) { case Page.LIST: - if (entry.type == extensions.ShowingType.APPS) - path = '/apps'; - else path = '/'; break; case Page.DETAILS: @@ -179,16 +201,17 @@ cr.define('extensions', function() { const state = {url: path}; const currentPage = this.getCurrentPage(); const isDialogNavigation = currentPage.page == entry.page && - currentPage.extensionId == entry.extensionId && - currentPage.type == entry.type; + currentPage.extensionId == entry.extensionId; // Navigating to a dialog doesn't visually change pages; it just opens // a dialog. As such, we replace state rather than pushing a new state // on the stack so that hitting the back button doesn't just toggle the // dialog. - if (isDialogNavigation) + if (replaceState || isDialogNavigation) { history.replaceState(state, '', path); - else + } else { + this.previousPage_ = currentPage; history.pushState(state, '', path); + } } } diff --git a/chromium/chrome/browser/resources/md_extensions/options_dialog.html b/chromium/chrome/browser/resources/md_extensions/options_dialog.html index 8dd57e9fd69..3c959b13644 100644 --- a/chromium/chrome/browser/resources/md_extensions/options_dialog.html +++ b/chromium/chrome/browser/resources/md_extensions/options_dialog.html @@ -3,6 +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="item_behavior.html"> <link rel="import" href="navigation_helper.html"> <dom-module id="extensions-options-dialog"> @@ -13,18 +14,28 @@ height: 32px; width: 32px; } + #icon-and-name-wrapper { align-items: center; display: flex; } - extensionoptions { + ExtensionOptions { display: block; + min-width: 300px; } dialog { - /* Initially as wide as possible. This will be adjusted by js-code. */ - width: 100%; + width: fit-content; + --cr-dialog-body: { + overflow: hidden; + padding: 0; + }; + + --cr-dialog-body-container: { + border: none; + min-height: initial; + }; } </style> @@ -32,7 +43,11 @@ on-close="onClose_"> <div slot="title"> <div id="icon-and-name-wrapper"> - <img id="icon" src="[[data_.iconUrl]]"> + <img id="icon" src="[[data_.iconUrl]]" + alt$="[[appOrExtension( + data.type, + '$i18nPolymer{appIcon}', + '$i18nPolymer{extensionIcon}')]]"> <span>[[data_.name]]</span> </div> </div> diff --git a/chromium/chrome/browser/resources/md_extensions/options_dialog.js b/chromium/chrome/browser/resources/md_extensions/options_dialog.js index 591c4b7049e..adffadd9da2 100644 --- a/chromium/chrome/browser/resources/md_extensions/options_dialog.js +++ b/chromium/chrome/browser/resources/md_extensions/options_dialog.js @@ -5,15 +5,11 @@ cr.define('extensions', function() { 'use strict'; - const MAX_HEIGHT = 600; - const MAX_WIDTH = 600; - const MIN_HEIGHT = 300; - const MIN_WIDTH = 300; - const HEADER_EXTRA_SPACING = 50; // 40 from x-button + 10 from img margin. - const DIALOG_PADDING = 32; // Padding from cr-dialog's .body styling. - const OptionsDialog = Polymer({ is: 'extensions-options-dialog', + + behaviors: [extensions.ItemBehavior], + properties: { /** @private {Object} */ extensionOptions_: Object, @@ -33,32 +29,22 @@ cr.define('extensions', function() { this.extensionOptions_ = document.createElement('ExtensionOptions'); this.extensionOptions_.extension = this.data_.id; this.extensionOptions_.onclose = this.close.bind(this); - const bounded = function(min, max, val) { - return Math.min(Math.max(min, val), max); - }; const onSizeChanged = e => { - const minHeaderWidth = - this.$$('#icon-and-name-wrapper img').offsetWidth + - this.$$('#icon-and-name-wrapper span').offsetWidth + - HEADER_EXTRA_SPACING; - const minWidth = Math.max(minHeaderWidth, MIN_WIDTH); - this.extensionOptions_.style.height = - bounded(MIN_HEIGHT, MAX_HEIGHT, e.height) + 'px'; - this.extensionOptions_.style.width = - bounded(minWidth, MAX_WIDTH, e.width) + 'px'; - this.$.dialog.style.width = - (bounded(minWidth, MAX_WIDTH, e.width) + DIALOG_PADDING) + 'px'; + this.extensionOptions_.style.height = e.height + 'px'; + this.extensionOptions_.style.width = e.width + 'px'; + + if (!this.$$('dialog').open) + this.$$('dialog').showModal(); }; this.extensionOptions_.onpreferredsizechanged = onSizeChanged; this.$.body.appendChild(this.extensionOptions_); - this.$$('dialog').showModal(); - onSizeChanged({height: 0, width: 0}); }, close: function() { this.$$('dialog').close(); + this.extensionOptions_.onpreferredsizechanged = null; }, /** @private */ diff --git a/chromium/chrome/browser/resources/md_extensions/pack_dialog.html b/chromium/chrome/browser/resources/md_extensions/pack_dialog.html index 2e11adbdf66..ac6a453d0c5 100644 --- a/chromium/chrome/browser/resources/md_extensions/pack_dialog.html +++ b/chromium/chrome/browser/resources/md_extensions/pack_dialog.html @@ -61,7 +61,7 @@ </dialog> <template is="dom-if" if="[[lastResponse_]]" restamp> <extensions-pack-dialog-alert model="[[lastResponse_]]" - on-warning-confirmed="onWarningConfirmed_" on-close="resetResponse_"> + on-close="onAlertClose_"> </extensions-pack-dialog-alert> </template> </template> diff --git a/chromium/chrome/browser/resources/md_extensions/pack_dialog.js b/chromium/chrome/browser/resources/md_extensions/pack_dialog.js index de6f9b3b2e7..f746508e512 100644 --- a/chromium/chrome/browser/resources/md_extensions/pack_dialog.js +++ b/chromium/chrome/browser/resources/md_extensions/pack_dialog.js @@ -89,25 +89,33 @@ cr.define('extensions', function() { * @private */ onPackResponse_: function(response) { - if (response.status === chrome.developerPrivate.PackStatus.SUCCESS) { - this.$.dialog.close(); - } else { - this.set('lastResponse_', response); - } + this.lastResponse_ = response; }, /** - * The handler function when user chooses to 'Proceed Anyway' upon - * receiving a waring. + * In the case that the alert dialog was a success message, the entire + * pack-dialog should close. Otherwise, we detach the alert by setting + * lastResponse_ null. Additionally, if the user selected "proceed anyway" + * in the warning dialog, we pack the extension again with override flags. + * @param {!Event} e * @private */ - onWarningConfirmed_: function() { - this.delegate.packExtension( - this.lastResponse_.item_path, this.lastResponse_.pem_path, - this.lastResponse_.override_flags, this.onPackResponse_.bind(this)); - }, + onAlertClose_: function(e) { + e.stopPropagation(); + + if (this.lastResponse_.status == + chrome.developerPrivate.PackStatus.SUCCESS) { + this.$.dialog.close(); + return; + } + + /* This is only possible for a warning dialog. */ + if (this.$$('extensions-pack-dialog-alert').returnValue == 'success') { + this.delegate.packExtension( + this.lastResponse_.item_path, this.lastResponse_.pem_path, + this.lastResponse_.override_flags, this.onPackResponse_.bind(this)); + } - resetResponse_: function() { this.lastResponse_ = null; }, }); 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 11db084bd02..68bc231c581 100644 --- a/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html +++ b/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html @@ -10,16 +10,19 @@ <dom-module id="extensions-pack-dialog-alert"> <template> <style include="cr-shared-style paper-button-style"> + .body { + white-space: pre-wrap; + word-break: break-word; + } </style> <dialog is="cr-dialog" id="dialog" close-text="$i18n{close}"> <div class="title" slot="title">[[title_]]</div> - <div class="body" slot="body"> - [[model.message]] - </div> + <!-- No whitespace or new-lines allowed within the div.body tag. --> + <div class="body" slot="body">[[model.message]]</div> <div class="button-container" slot="button-container"> - <paper-button class="cancel-button" on-tap="onCancelTap_" - hidden="[[!cancelLabel_]]"> + <paper-button class$="[[getCancelButtonClass_(confirmLabel_)]]" + on-tap="onCancelTap_" hidden="[[!cancelLabel_]]"> [[cancelLabel_]] </paper-button> <paper-button class="action-button" on-tap="onConfirmTap_" diff --git a/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.js b/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.js index 3afe8734260..7c3e475dd27 100644 --- a/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.js +++ b/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.js @@ -20,8 +20,19 @@ cr.define('extensions', function() { /** @private */ cancelLabel_: String, - /** @private */ - confirmLabel_: String, + /** + * This needs to be initialized to trigger data-binding. + * @private + */ + confirmLabel_: { + type: String, + value: '', + } + }, + + /** @return {string} */ + get returnValue() { + return this.$.dialog.returnValue; }, /** @override */ @@ -41,8 +52,10 @@ cr.define('extensions', function() { this.title_ = loadTimeData.getString('packDialogErrorTitle'); this.cancelLabel_ = loadTimeData.getString('ok'); break; - // If status were success, this dialog should not be attached at all. case chrome.developerPrivate.PackStatus.SUCCESS: + this.title_ = loadTimeData.getString('packDialogTitle'); + this.cancelLabel_ = loadTimeData.getString('ok'); + break; default: assertNotReached(); return; @@ -54,6 +67,14 @@ cr.define('extensions', function() { this.$.dialog.showModal(); }, + /** + * @return {string} + * @private + */ + getCancelButtonClass_: function() { + return this.confirmLabel_ ? 'cancel-button' : 'action-button'; + }, + /** @private */ onCancelTap_: function() { this.$.dialog.cancel(); @@ -63,7 +84,6 @@ cr.define('extensions', function() { onConfirmTap_: function() { // The confirm button should only be available in WARNING state. assert(this.model.status === chrome.developerPrivate.PackStatus.WARNING); - this.fire('warning-confirmed'); this.$.dialog.close(); } }); diff --git a/chromium/chrome/browser/resources/md_extensions/service.js b/chromium/chrome/browser/resources/md_extensions/service.js index 912dd3e9f54..4d48b0678b5 100644 --- a/chromium/chrome/browser/resources/md_extensions/service.js +++ b/chromium/chrome/browser/resources/md_extensions/service.js @@ -16,100 +16,34 @@ cr.define('extensions', function() { constructor() { /** @private {boolean} */ this.isDeleting_ = false; - - /** @private {extensions.Manager} */ - this.manager_; - - /** @private {Array<chrome.developerPrivate.ExtensionInfo>} */ - this.extensions_; } - /** @param {extensions.Manager} manager */ - managerReady(manager) { - 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; - } + getProfileConfiguration() { + return new Promise(function(resolve, reject) { + chrome.developerPrivate.getProfileConfiguration(resolve); + }); + } - const keyboardShortcuts = this.manager_.keyboardShortcuts; - keyboardShortcuts.addEventListener( - 'shortcut-updated', this.onExtensionCommandUpdated_.bind(this)); - keyboardShortcuts.addEventListener( - 'shortcut-capture-started', - this.onShortcutCaptureChanged_.bind(this, true)); - keyboardShortcuts.addEventListener( - 'shortcut-capture-ended', - this.onShortcutCaptureChanged_.bind(this, false)); - chrome.developerPrivate.onProfileStateChanged.addListener( - this.onProfileStateChanged_.bind(this)); - chrome.developerPrivate.onItemStateChanged.addListener( - this.onItemStateChanged_.bind(this)); - chrome.developerPrivate.getExtensionsInfo( - {includeDisabled: true, includeTerminated: true}, extensions => { - this.extensions_ = extensions; - for (let extension of extensions) - this.manager_.addItem(extension); - - this.manager_.initPage(); - }); - chrome.developerPrivate.getProfileConfiguration( - this.onProfileStateChanged_.bind(this)); + getItemStateChangedTarget() { + return chrome.developerPrivate.onItemStateChanged; } - /** - * @param {chrome.developerPrivate.ProfileInfo} profileInfo - * @private - */ - onProfileStateChanged_(profileInfo) { - this.manager_.set('inDevMode', profileInfo.inDeveloperMode); + getProfileStateChangedTarget() { + return chrome.developerPrivate.onProfileStateChanged; } - /** - * @param {chrome.developerPrivate.EventData} eventData - * @private - */ - onItemStateChanged_(eventData) { - const currentIndex = this.extensions_.findIndex(function(extension) { - return extension.id == eventData.item_id; + getExtensionsInfo() { + return new Promise(function(resolve, reject) { + chrome.developerPrivate.getExtensionsInfo( + {includeDisabled: true, includeTerminated: true}, resolve); }); + } - const EventType = chrome.developerPrivate.EventType; - switch (eventData.event_type) { - case EventType.VIEW_REGISTERED: - case EventType.VIEW_UNREGISTERED: - case EventType.INSTALLED: - case EventType.LOADED: - case EventType.UNLOADED: - case EventType.ERROR_ADDED: - case EventType.ERRORS_REMOVED: - case EventType.PREFS_CHANGED: - // |extensionInfo| can be undefined in the case of an extension - // being unloaded right before uninstallation. There's nothing to do - // here. - if (!eventData.extensionInfo) - break; - - if (currentIndex >= 0) { - this.extensions_[currentIndex] = eventData.extensionInfo; - this.manager_.updateItem(eventData.extensionInfo); - } else { - this.extensions_.push(eventData.extensionInfo); - this.manager_.addItem(eventData.extensionInfo); - } - break; - case EventType.UNINSTALLED: - this.manager_.removeItem(this.extensions_[currentIndex]); - this.extensions_.splice(currentIndex, 1); - break; - default: - assertNotReached(); - } + /** @override */ + getExtensionSize(id) { + return new Promise(function(resolve, reject) { + chrome.developerPrivate.getExtensionSize(id, resolve); + }); } /** @@ -135,14 +69,15 @@ cr.define('extensions', function() { /** * Updates an extension command. - * @param {!CustomEvent} e - * @private + * @param {string} extensionId + * @param {string} commandName + * @param {string} keybinding */ - onExtensionCommandUpdated_(e) { + updateExtensionCommand(extensionId, commandName, keybinding) { chrome.developerPrivate.updateExtensionCommand({ - extensionId: e.detail.item, - commandName: e.detail.commandName, - keybinding: e.detail.keybinding, + extensionId: extensionId, + commandName: commandName, + keybinding: keybinding, }); } @@ -154,10 +89,8 @@ cr.define('extensions', function() { * the default handling on the event also does this. Investigate more in the * future. * @param {boolean} isCapturing - * @param {!CustomEvent} e - * @private */ - onShortcutCaptureChanged_(isCapturing, e) { + setShortcutHandlingSuspended(isCapturing) { chrome.developerPrivate.setShortcutHandlingSuspended(isCapturing); } @@ -165,22 +98,26 @@ cr.define('extensions', function() { * Attempts to load an unpacked extension, optionally as another attempt at * a previously-specified load. * @param {string=} opt_retryGuid + * @return {!Promise} A signal that loading finished, rejected if any error + * occured. * @private */ loadUnpackedHelper_(opt_retryGuid) { - chrome.developerPrivate.loadUnpacked( - {failQuietly: true, populateError: true, retryGuid: opt_retryGuid}, - (loadError) => { - if (chrome.runtime.lastError && - chrome.runtime.lastError.message != - 'File selection was canceled.') { - throw new Error(chrome.runtime.lastError.message); - } - if (loadError) { - this.manager_.loadError.loadError = loadError; - this.manager_.loadError.show(); - } - }); + return new Promise(function(resolve, reject) { + chrome.developerPrivate.loadUnpacked( + {failQuietly: true, populateError: true, retryGuid: opt_retryGuid}, + (loadError) => { + if (chrome.runtime.lastError && + chrome.runtime.lastError.message != + 'File selection was canceled.') { + throw new Error(chrome.runtime.lastError.message); + } + if (loadError) + return reject(loadError); + + resolve(); + }); + }); } /** @override */ @@ -244,9 +181,28 @@ cr.define('extensions', function() { }); } + /** + * @param {string} url + * @override + */ + openUrl(url) { + window.open(url); + } + /** @override */ reloadItem(id) { - chrome.developerPrivate.reload(id, {failQuietly: false}); + return new Promise(function(resolve, reject) { + chrome.developerPrivate.reload( + id, {failQuietly: true, populateErrorForUnpacked: true}, + (loadError) => { + if (loadError) { + reject(loadError); + return; + } + + resolve(); + }); + }); } /** @override */ @@ -255,16 +211,16 @@ cr.define('extensions', function() { } /** @override */ - showItemOptionsPage(id) { - const extension = this.extensions_.find(function(e) { - return e.id == id; - }); + showItemOptionsPage(extension) { assert(extension && extension.optionsPage); if (extension.optionsPage.openInTab) { - chrome.developerPrivate.showOptions(id); + chrome.developerPrivate.showOptions(extension.id); } else { - extensions.navigation.navigateTo( - {page: Page.DETAILS, subpage: Dialog.OPTIONS, extensionId: id}); + extensions.navigation.navigateTo({ + page: Page.DETAILS, + subpage: Dialog.OPTIONS, + extensionId: extension.id, + }); } } @@ -276,12 +232,12 @@ cr.define('extensions', function() { /** @override */ loadUnpacked() { - this.loadUnpackedHelper_(); + return this.loadUnpackedHelper_(); } /** @override */ retryLoadUnpacked(retryGuid) { - this.loadUnpackedHelper_(retryGuid); + return this.loadUnpackedHelper_(retryGuid); } /** @override */ @@ -306,6 +262,7 @@ cr.define('extensions', function() { /** @override */ updateAllExtensions() { chrome.developerPrivate.autoUpdate(); + chrome.metricsPrivate.recordUserAction('Options_UpdateExtensions'); } /** @override */ @@ -330,6 +287,11 @@ cr.define('extensions', function() { openDevTools(args) { chrome.developerPrivate.openDevTools(args); } + + /** @override */ + showInFolder(id) { + chrome.developerPrivate.showPath(id); + } } 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 faf98ef6f44..ad5c9bb03b9 100644 --- a/chromium/chrome/browser/resources/md_extensions/shortcut_input.html +++ b/chromium/chrome/browser/resources/md_extensions/shortcut_input.html @@ -8,6 +8,7 @@ <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-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="chrome://resources/polymer/v1_0/paper-styles/color.html"> <link rel="import" href="shortcut_util.html"> @@ -24,7 +25,7 @@ margin-bottom: 0px; margin-top: 2px; /* Offset underline spacing. */ padding: 0; - @apply(--cr-primary-text); + @apply --cr-primary-text; }; } @@ -37,8 +38,12 @@ </style> <div id="main"> <paper-input id="input" placeholder="$i18n{shortcutTypeAShortcut}" - value="[[computeText_(capturing_, shortcut, pendingShortcut_)]]" - no-label-float> + error-message="[[getErrorString_(error_, + '$i18nPolymer{shortcutIncludeStartModifier}', + '$i18nPolymer{shortcutTooManyModifiers}', + '$i18nPolymer{shortcutNeedCharacter}')]]" + 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_" diff --git a/chromium/chrome/browser/resources/md_extensions/shortcut_input.js b/chromium/chrome/browser/resources/md_extensions/shortcut_input.js index a6abd7b3e75..d70ebd02624 100644 --- a/chromium/chrome/browser/resources/md_extensions/shortcut_input.js +++ b/chromium/chrome/browser/resources/md_extensions/shortcut_input.js @@ -5,6 +5,14 @@ cr.define('extensions', function() { 'use strict'; + /** @enum {number} */ + const ShortcutError = { + NO_ERROR: 0, + INCLUDE_START_MODIFIER: 1, + TOO_MANY_MODIFIERS: 2, + NEED_CHARACTER: 3, + }; + // The UI to display and manage keyboard shortcuts set for extension commands. const ShortcutInput = Polymer({ is: 'extensions-shortcut-input', @@ -12,23 +20,36 @@ cr.define('extensions', function() { behaviors: [I18nBehavior], properties: { + /** @type {!Object} */ + delegate: Object, + item: { type: String, value: '', }, + commandName: { type: String, value: '', }, + shortcut: { type: String, value: '', }, + /** @private */ capturing_: { type: Boolean, value: false, }, + + /** @private {!ShortcutError} */ + error_: { + type: Number, + value: 0, + }, + /** @private */ pendingShortcut_: { type: String, @@ -36,6 +57,7 @@ cr.define('extensions', function() { }, }, + /** @override */ ready: function() { const node = this.$['input']; node.addEventListener('mouseup', this.startCapture_.bind(this)); @@ -50,7 +72,7 @@ cr.define('extensions', function() { if (this.capturing_) return; this.capturing_ = true; - this.fire('shortcut-capture-started'); + this.delegate.setShortcutHandlingSuspended(true); }, /** @private */ @@ -59,8 +81,10 @@ cr.define('extensions', function() { return; this.pendingShortcut_ = ''; this.capturing_ = false; - this.$['input'].blur(); - this.fire('shortcut-capture-ended'); + const input = this.$.input; + input.blur(); + input.invalid = false; + this.delegate.setShortcutHandlingSuspended(false); }, /** @@ -102,6 +126,23 @@ cr.define('extensions', function() { }, /** + * @param {!ShortcutError} error + * @param {string} includeStartModifier + * @param {string} tooManyModifiers + * @param {string} needCharacter + * @return {string} UI string. + * @private + */ + getErrorString_: function( + error, includeStartModifier, tooManyModifiers, needCharacter) { + if (error == ShortcutError.TOO_MANY_MODIFIERS) + return tooManyModifiers; + if (error == ShortcutError.NEED_CHARACTER) + return needCharacter; + return includeStartModifier; + }, + + /** * @param {!KeyboardEvent} e * @private */ @@ -115,27 +156,34 @@ cr.define('extensions', function() { // We don't allow both Ctrl and Alt in the same keybinding. // TODO(devlin): This really should go in extensions.hasValidModifiers, // but that requires updating the existing page as well. - if ((e.ctrlKey && e.altKey) || !extensions.hasValidModifiers(e)) { - this.pendingShortcut_ = 'invalid'; + if (e.ctrlKey && e.altKey) { + this.error_ = ShortcutError.TOO_MANY_MODIFIERS; + this.$.input.invalid = true; + return; + } + if (!extensions.hasValidModifiers(e)) { + this.pendingShortcut_ = ''; + this.error_ = ShortcutError.INCLUDE_START_MODIFIER; + this.$.input.invalid = true; return; } - this.pendingShortcut_ = extensions.keystrokeToString(e); - - if (extensions.isValidKeyCode(e.keyCode)) { - this.commitPending_(); - this.endCapture_(); + if (!extensions.isValidKeyCode(e.keyCode)) { + this.error_ = ShortcutError.NEED_CHARACTER; + this.$.input.invalid = true; + return; } + this.$.input.invalid = false; + + this.commitPending_(); + this.endCapture_(); }, /** @private */ commitPending_: function() { this.shortcut = this.pendingShortcut_; - this.fire('shortcut-updated', { - keybinding: this.shortcut, - item: this.item, - commandName: this.commandName - }); + this.delegate.updateExtensionCommand( + this.item, this.commandName, this.shortcut); }, /** diff --git a/chromium/chrome/browser/resources/md_extensions/sidebar.html b/chromium/chrome/browser/resources/md_extensions/sidebar.html index bd1887dd6fd..1d49eeec145 100644 --- a/chromium/chrome/browser/resources/md_extensions/sidebar.html +++ b/chromium/chrome/browser/resources/md_extensions/sidebar.html @@ -1,74 +1,84 @@ <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/html/cr.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-item/paper-item.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-menu/paper-menu.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-selector/iron-selector.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> <link rel="import" href="navigation_helper.html"> <dom-module id="extensions-sidebar"> <template> - <style> + <style include="cr-icons"> :host { - display: flex; - flex-direction: column; + display: block; height: 100%; - white-space: nowrap; + overflow-x: hidden; + overflow-y: auto; + width: 256px; } - #section-menu { - --paper-menu-background-color: transparent; - --paper-menu-selected-item: { - background-color: transparent; - color: var(--google-blue-700); - font-weight: normal; - } + .cr-icon { + -webkit-margin-end: calc( + var(--cr-section-padding) - var(--cr-icon-ripple-padding)); + } - padding: 0; + iron-selector .iron-selected { + color: var(--google-blue-700); } - #more-extensions { - -webkit-padding-end: 16px; - align-items: center; + iron-selector .separator { + background-color: rgba(0, 0, 0, 0.08); + flex-shrink: 0; + height: 1px; + margin: 8px 0; + } + + #sectionMenu { + background-color: transparent; + color: #5a5a5a; display: flex; - justify-content: space-between; - text-decoration: none; + flex: 1; + flex-direction: column; + padding-top: 8px; + user-select: none; } .section-item { + /* Ensure the focus outline appears correctly (crbug.com/655503). */ + -webkit-margin-end: 4px; -webkit-padding-start: 24px; - color: #5A5A5A; + align-items: center; + box-sizing: border-box; + color: inherit; cursor: pointer; - height: 48px; - } - - button[is='paper-icon-button-light'].open-in-new { - background-image: url(chrome://resources/images/open_in_new.svg); - background-size: contain; - height: 15px; - width: 15px; + display: flex; + font-weight: 500; + justify-content: space-between; + min-height: 40px; + position: relative; + text-decoration: none; } </style> - <paper-menu id="section-menu" selected="{{selected_}}"> - <paper-item class="section-item" id="sections-extensions" + <iron-selector id="sectionMenu"> + <div class="section-item" id="sections-extensions" on-tap="onExtensionsTap_"> - <span>$i18n{sidebarExtensions}</span> - </paper-item> - <paper-item class="section-item" id="sections-apps" - on-tap="onAppsTap_"> - <span>$i18n{sidebarApps}</span> - </paper-item> - <paper-item class="section-item" id="sections-shortcuts" + $i18n{sidebarExtensions} + <paper-ripple></paper-ripple> + </div> + <div class="section-item" id="sections-shortcuts" on-tap="onKeyboardShortcutsTap_"> - <span>$i18n{keyboardShortcuts}</span> - </paper-item> - </paper-menu> - <a class="section-item" id="more-extensions" target="_blank" - href="$i18n{getMoreExtensionsUrl}"> - <span>$i18n{getMoreExtensions}</span> - <button class="open-in-new" is="paper-icon-button-light"></button> - </a> + $i18n{keyboardShortcuts} + <paper-ripple></paper-ripple> + </div> + <div class="separator"></div> + <a class="section-item" id="more-extensions" target="_blank" + href="$i18n{getMoreExtensionsUrl}" on-tap="onMoreExtensionsTap_"> + <span>$i18n{openChromeWebStore}</span> + <div class="cr-icon icon-external"></div> + <paper-ripple></paper-ripple> + </a> + </iron-selector> </template> <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 a2fea0dfc20..5f32ee1db98 100644 --- a/chromium/chrome/browser/resources/md_extensions/sidebar.js +++ b/chromium/chrome/browser/resources/md_extensions/sidebar.js @@ -5,28 +5,20 @@ cr.define('extensions', function() { const Sidebar = Polymer({ is: 'extensions-sidebar', - properties: { - /** @private {number} */ - selected_: { - type: Number, - value: -1, - }, - }, - hostAttributes: { role: 'navigation', }, - /** @private */ - onExtensionsTap_: function() { - extensions.navigation.navigateTo( - {page: Page.LIST, type: extensions.ShowingType.EXTENSIONS}); + /** @override */ + attached: function() { + this.$.sectionMenu.select( + extensions.navigation.getCurrentPage().page == Page.SHORTCUTS ? 1 : + 0); }, /** @private */ - onAppsTap_: function() { - extensions.navigation.navigateTo( - {page: Page.LIST, type: extensions.ShowingType.APPS}); + onExtensionsTap_: function() { + extensions.navigation.navigateTo({page: Page.LIST}); }, /** @private */ @@ -34,28 +26,9 @@ cr.define('extensions', 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; + /** @private */ + onMoreExtensionsTap_: function() { + chrome.metricsPrivate.recordUserAction('Options_GetMoreExtensions'); }, }); diff --git a/chromium/chrome/browser/resources/md_extensions/toggle_row.html b/chromium/chrome/browser/resources/md_extensions/toggle_row.html new file mode 100644 index 00000000000..b326dc8ca00 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/toggle_row.html @@ -0,0 +1,49 @@ +<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/shared_style_css.html"> + +<dom-module id="extensions-toggle-row"> + <template> + <style> + :host { + @apply --cr-section; + flex-direction: column; + padding-left: 0; + padding-right: 0; + touch-action: none; + } + + input { + display: none; + } + + label { + align-items: center; + box-sizing: border-box; + cursor: pointer; + display: flex; + flex: 1; + padding: 0 var(--cr-section-padding); + width: 100%; + } + + cr-toggle { + display: inline-block; + } + + :host ::slotted(*) { + -webkit-margin-end: 20px; + flex: 1; + } + </style> + <label id="label" on-click="onLabelTap_"> + <input id="native" type="checkbox" checked="[[checked]]" + on-change="onNativeChange_" on-click="onNativeClick_"> + <slot></slot> + <cr-toggle id="crToggle" checked="{{checked}}" aria-labelledby="label" + on-change="onCrToggleChange_"></cr-toggle> + </label> + </template> + <script src="toggle_row.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/toggle_row.js b/chromium/chrome/browser/resources/md_extensions/toggle_row.js new file mode 100644 index 00000000000..8667db24ce4 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/toggle_row.js @@ -0,0 +1,85 @@ +// 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'; + + /** + * An extensions-toggle-row provides a way of having a clickable row that can + * modify a cr-toggle, by leveraging the native <label> functionality. It uses + * a hidden native <input type="checkbox"> to achieve this. + */ + const ExtensionsToggleRow = Polymer({ + is: 'extensions-toggle-row', + + properties: { + checked: Boolean, + }, + + /** + * Exposing the clickable part of extensions-toggle-row for testing + * purposes. + * @return {!HTMLElement} + */ + getLabel() { + return this.$.label; + }, + + /** + * @param {!Event} + * @private + */ + onLabelTap_: function(e) { + // If the interaction sequence (pointerdown+pointerup) began within the + // cr-toggle itself, then prevent this event from changing the state of + // the toggle. + if (this.$.crToggle.shouldIgnoreHostTap(e)) + e.preventDefault(); + }, + + /** + * @param {!Event} + * @private + */ + onNativeClick_: function(e) { + // Even though the native checkbox is hidden and can't be actually + // cilcked/tapped by the user, because it resides within the <label> the + // browser emits an extraneous event when the label is clicked. Stop + // propagation so that it does not interfere with |onLabelTap_| listener. + e.stopPropagation(); + }, + + /** + * Fires when the native checkbox changes value. This happens when the user + * clicks directly on the <label>. + * @param {!Event} e + * @private + */ + onNativeChange_: function(e) { + e.stopPropagation(); + + // Sync value of native checkbox and cr-toggle and |checked|. + this.$.crToggle.checked = this.$.native.checked; + this.checked = this.$.native.checked; + + this.fire('change', this.checked); + }, + + /** + * Fires + * @param {!CustomEvent} e + * @private + */ + onCrToggleChange_: function(e) { + e.stopPropagation(); + + // Sync value of native checkbox and cr-toggle. + this.$.native.checked = e.detail; + + this.fire('change', this.checked); + }, + }); + + return {ExtensionsToggleRow: ExtensionsToggleRow}; +}); diff --git a/chromium/chrome/browser/resources/md_extensions/toolbar.html b/chromium/chrome/browser/resources/md_extensions/toolbar.html index c57b165e06f..5e90a25a03c 100644 --- a/chromium/chrome/browser/resources/md_extensions/toolbar.html +++ b/chromium/chrome/browser/resources/md_extensions/toolbar.html @@ -14,12 +14,14 @@ <template> <style include="cr-hidden-style paper-button-style"> :host { - --toolbar-width: 580px; + /* The constant is the height of the tallest control. */ + --button-row-height: calc(var(--margin-bottom) + 36px); + --drawer-transition: 0.3s cubic-bezier(.25, .1, .25, 1); + --margin-bottom: 4px; --toolbar-color: var(--md-toolbar-color); } cr-toolbar { - --cr-toolbar-field-width: var(--toolbar-width); background: var(--toolbar-color); } @@ -31,24 +33,42 @@ --cr-toggle-unchecked-ink-color: white; } - .dev-controls { - background: var(--toolbar-color); - display: flex; - justify-content: center; - width: 100%; + #devDrawer[expanded] #button-strip { + top: 0; + } + + #devDrawer { + height: 0; + overflow: hidden; + position: relative; + transition: height var(--drawer-transition); + } + + #devDrawer[expanded] { + height: var(--button-row-height); } #button-strip { - margin-bottom: 4px; - /* We left-align the text of the left button with the left edge of the - search field. Since the buttons have 12px padding, add 24px to the - width of the button strip (12px each side) to make centering easy. */ - width: calc(var(--toolbar-width) + 2 * 12px); + -webkit-margin-end: auto; + -webkit-margin-start: auto; + background: var(--toolbar-color); + margin-bottom: var(--margin-bottom); + position: absolute; + text-align: center; + top: calc(var(--button-row-height) * -1); + transition: top var(--drawer-transition); + /* Prevent selection of the blank space between buttons. */ + user-select: none; + width: 100%; } #button-strip paper-button { -webkit-margin-end: 16px; color: white; + /* Increase contrast compared to default values. */ + --paper-button-flat-keyboard-focus: { + background: rgba(0, 0, 0, .3); + }; } .more-actions { @@ -65,15 +85,16 @@ search-prompt="$i18n{search}" clear-label="$i18n{clearSearch}" menu-label="$i18n{mainMenu}" - show-menu="[[!isGuest]]" show-search="[[!isGuest]]"> + show-menu="[[!isGuest]]" show-search="[[!isGuest]]" + narrow-threshold="1000"> <div class="more-actions" hidden$="[[isGuest]]"> <span id="devModeLabel">$i18n{toolbarDevMode}</span> - <cr-toggle id="dev-mode" on-change="onDevModeChange_" + <cr-toggle id="dev-mode" on-change="onDevModeToggleChange_" checked="[[inDevMode]]" aria-labelledby="devModeLabel"> </cr-toggle> </div> </cr-toolbar> - <div class="dev-controls" hidden$="[[!inDevMode]]"> + <div id="devDrawer" expanded$="[[expanded_]]"> <div id="button-strip"> <paper-button id="load-unpacked" on-tap="onLoadUnpackedTap_"> $i18n{toolbarLoadUnpacked} @@ -81,7 +102,8 @@ <paper-button id="pack-extensions" on-tap="onPackTap_"> $i18n{toolbarPack} </paper-button> - <paper-button id="update-now" on-tap="onUpdateNowTap_"> + <paper-button id="update-now" on-tap="onUpdateNowTap_" + title="$i18n{toolbarUpdateNowTooltip}"> $i18n{toolbarUpdateNow} </paper-button> <if expr="chromeos"> diff --git a/chromium/chrome/browser/resources/md_extensions/toolbar.js b/chromium/chrome/browser/resources/md_extensions/toolbar.js index f973f10ed3e..2de38b38cc1 100644 --- a/chromium/chrome/browser/resources/md_extensions/toolbar.js +++ b/chromium/chrome/browser/resources/md_extensions/toolbar.js @@ -13,7 +13,10 @@ cr.define('extensions', function() { */ setProfileInDevMode(inDevMode) {} - /** Opens the dialog to load unpacked extensions. */ + /** + * Opens the dialog to load unpacked extensions. + * @return {!Promise} + */ loadUnpacked() {} /** Updates all extensions. */ @@ -32,6 +35,7 @@ cr.define('extensions', function() { inDevMode: { type: Boolean, value: false, + observer: 'onInDevModeChanged_', }, isGuest: Boolean, @@ -39,25 +43,59 @@ cr.define('extensions', function() { // <if expr="chromeos"> kioskEnabled: Boolean, // </if> + + /** @private */ + expanded_: { + type: Boolean, + value: false, + }, }, hostAttributes: { role: 'banner', }, + /** @override */ + ready: function() { + this.$.devDrawer.addEventListener('transitionend', () => { + this.delegate.setProfileInDevMode(this.$['dev-mode'].checked); + }); + }, + + /** @private */ + onDevModeToggleChange_: function() { + const drawer = this.$.devDrawer; + if (drawer.hidden) { + drawer.hidden = false; + // Requesting the offsetTop will cause a reflow (to account for hidden). + /** @suppress {suspiciousCode} */ drawer.offsetTop; + } + this.expanded_ = !this.expanded_; + + chrome.metricsPrivate.recordUserAction( + 'Options_ToggleDeveloperMode_' + + (this.expanded_ ? 'Enabled' : 'Disabled')); + }, + /** @private */ - onDevModeChange_: function() { - this.delegate.setProfileInDevMode(this.$['dev-mode'].checked); + onInDevModeChanged_: function() { + // Set the initial state. + this.expanded_ = this.inDevMode; + this.$.devDrawer.hidden = !this.inDevMode; }, /** @private */ onLoadUnpackedTap_: function() { - this.delegate.loadUnpacked(); + this.delegate.loadUnpacked().catch(loadError => { + this.fire('load-error', loadError); + }); + chrome.metricsPrivate.recordUserAction('Options_LoadUnpackedExtension'); }, /** @private */ onPackTap_: function() { this.fire('pack-tap'); + chrome.metricsPrivate.recordUserAction('Options_PackExtension'); }, // <if expr="chromeos"> diff --git a/chromium/chrome/browser/resources/md_extensions/view_manager.js b/chromium/chrome/browser/resources/md_extensions/view_manager.js index 36e5383065f..011f357c366 100644 --- a/chromium/chrome/browser/resources/md_extensions/view_manager.js +++ b/chromium/chrome/browser/resources/md_extensions/view_manager.js @@ -71,19 +71,22 @@ cr.define('extensions', function() { }, /** - * @param {!Element} element - * @param {!string} animation + * @param {!Element} view + * @param {string} animation * @return {!Promise} * @private */ - enter_: function(element, animation) { + enter_: function(view, animation) { const animationFunction = extensions.viewAnimations.get(animation); assert(animationFunction); - element.classList.add('active'); - element.dispatchEvent(new CustomEvent('view-enter-start')); - return animationFunction(element).then(function() { - element.dispatchEvent(new CustomEvent('view-enter-finish')); + let effectiveView = + view.matches('[is=cr-lazy-render]') ? view.get() : view; + + effectiveView.classList.add('active'); + effectiveView.dispatchEvent(new CustomEvent('view-enter-start')); + return animationFunction(effectiveView).then(() => { + effectiveView.dispatchEvent(new CustomEvent('view-enter-finish')); }); }, @@ -107,20 +110,7 @@ cr.define('extensions', function() { return Promise.all(promises); }, - - /** - * Helper function to only animate the current view. - * @param {string} animation - * @return {!Promise} - */ - animateCurrentView: function(animation) { - const currentView = assert(this.querySelector('.active')); - const animationFunction = extensions.viewAnimations.get(animation); - assert(animationFunction); - - return animationFunction(currentView); - }, }); return {viewAnimations: viewAnimations, ViewManager: ViewManager}; -});
\ No newline at end of file +}); diff --git a/chromium/chrome/browser/resources/md_history/OWNERS b/chromium/chrome/browser/resources/md_history/OWNERS index c85d9424611..5e21905007d 100644 --- a/chromium/chrome/browser/resources/md_history/OWNERS +++ b/chromium/chrome/browser/resources/md_history/OWNERS @@ -1,4 +1,3 @@ calamity@chromium.org -tsergeant@chromium.org # COMPONENT: UI>Browser>History diff --git a/chromium/chrome/browser/resources/md_history/app.html b/chromium/chrome/browser/resources/md_history/app.html index ddd7280cc46..1d5e64876ae 100644 --- a/chromium/chrome/browser/resources/md_history/app.html +++ b/chromium/chrome/browser/resources/md_history/app.html @@ -21,6 +21,7 @@ color: var(--primary-text-color); display: block; height: 100%; + line-height: 1.54; /* 20px. */ overflow: hidden; } diff --git a/chromium/chrome/browser/resources/md_history/browser_service.js b/chromium/chrome/browser/resources/md_history/browser_service.js index 9ee3c8ebdfa..2757ff25868 100644 --- a/chromium/chrome/browser/resources/md_history/browser_service.js +++ b/chromium/chrome/browser/resources/md_history/browser_service.js @@ -97,7 +97,7 @@ cr.define('md_history', function() { */ recordAction: function(action) { if (action.indexOf('_') == -1) - action = 'HistoryPage_' + action; + action = `HistoryPage_${action}`; chrome.send('metricsHandler:recordAction', [action]); }, diff --git a/chromium/chrome/browser/resources/md_history/history_item.html b/chromium/chrome/browser/resources/md_history/history_item.html index 0bb3bbe614c..958032b7386 100644 --- a/chromium/chrome/browser/resources/md_history/history_item.html +++ b/chromium/chrome/browser/resources/md_history/history_item.html @@ -22,14 +22,6 @@ pointer-events: none; } - /** Unresolved paper-icon-button-light styles. */ - button { - background: none; - border: none; - outline: none; - padding: 0; - } - #main-container { position: relative; } @@ -76,6 +68,8 @@ #checkbox { height: 40px; + margin: 0; + padding: 0; width: 40px; } diff --git a/chromium/chrome/browser/resources/md_history/history_list.html b/chromium/chrome/browser/resources/md_history/history_list.html index 15d42bbae08..b27ce8d74e2 100644 --- a/chromium/chrome/browser/resources/md_history/history_list.html +++ b/chromium/chrome/browser/resources/md_history/history_list.html @@ -75,6 +75,8 @@ <template is="cr-lazy-render" id="sharedMenu"> <dialog is="cr-action-menu"> <button id="menuMoreButton" class="dropdown-item" + hidden="[[!canSearchMoreFromSite_( + searchedTerm, actionMenuModel_.item.domain)]]" on-tap="onMoreFromSiteTap_"> $i18n{moreFromSite} </button> diff --git a/chromium/chrome/browser/resources/md_history/history_list.js b/chromium/chrome/browser/resources/md_history/history_list.js index c9a11568cef..748e50ef62f 100644 --- a/chromium/chrome/browser/resources/md_history/history_list.js +++ b/chromium/chrome/browser/resources/md_history/history_list.js @@ -182,7 +182,7 @@ Polymer({ * @private */ changeSelection_: function(index, selected) { - this.set('historyData_.' + index + '.selected', selected); + this.set(`historyData_.${index}.selected`, selected); if (selected) this.selectedItems.add(index); else @@ -198,7 +198,7 @@ Polymer({ */ deleteSelected_: function() { var toBeRemoved = Array.from(this.selectedItems.values()) - .map((index) => this.get('historyData_.' + index)); + .map((index) => this.get(`historyData_.${index}`)); md_history.BrowserService.getInstance() .deleteItems(toBeRemoved) @@ -281,7 +281,7 @@ Polymer({ for (var i = 0; i < this.historyData_.length; i++) { if (this.historyData_[i].url == url) - this.set('historyData_.' + i + '.starred', false); + this.set(`historyData_.${i}.starred`, false); } }, @@ -468,6 +468,16 @@ Polymer({ }, /** + * @param {string} searchedTerm + * @param {string} domain + * @return {boolean} + * @private + */ + canSearchMoreFromSite_: function(searchedTerm, domain) { + return searchedTerm === '' || searchedTerm !== domain; + }, + + /** * @param {HistoryQuery} info * @param {!Array<HistoryEntry>} results * @private diff --git a/chromium/chrome/browser/resources/md_history/history_toolbar.html b/chromium/chrome/browser/resources/md_history/history_toolbar.html index 12fd39cba14..e9a28f30b9f 100644 --- a/chromium/chrome/browser/resources/md_history/history_toolbar.html +++ b/chromium/chrome/browser/resources/md_history/history_toolbar.html @@ -45,7 +45,7 @@ hidden$="[[itemsSelected_]]" spinner-active="[[spinnerActive]]" show-menu="[[hasDrawer]]" - show-menu-promo="[[showMenuPromo]]" + show-menu-promo="[[canShowMenuPromo_(showMenuPromo)]]" menu-label="$i18n{historyMenuButton}" menu-promo="$i18n{menuPromo}" close-menu-promo="$i18n{closeMenuPromo}" diff --git a/chromium/chrome/browser/resources/md_history/history_toolbar.js b/chromium/chrome/browser/resources/md_history/history_toolbar.js index 7b70d8ea149..3eeab953ba9 100644 --- a/chromium/chrome/browser/resources/md_history/history_toolbar.js +++ b/chromium/chrome/browser/resources/md_history/history_toolbar.js @@ -92,6 +92,15 @@ Polymer({ }, /** + * @param {boolean} showMenuPromo + * @return {boolean} + * @private + */ + canShowMenuPromo_: function(showMenuPromo) { + return this.showMenuPromo && !loadTimeData.getBoolean('isGuestSession'); + }, + + /** * @param {!CustomEvent} event * @private */ diff --git a/chromium/chrome/browser/resources/md_history/shared_style.html b/chromium/chrome/browser/resources/md_history/shared_style.html index 32534a40cca..5961b1553b5 100644 --- a/chromium/chrome/browser/resources/md_history/shared_style.html +++ b/chromium/chrome/browser/resources/md_history/shared_style.html @@ -6,6 +6,7 @@ <style include="cr-hidden-style"> a { color: var(--link-color); + text-decoration: none; } .card-title { @@ -50,7 +51,7 @@ white-space: nowrap; } - button.icon-button { + button[is='paper-icon-button-light'] { background: none; border: none; height: 36px; @@ -58,15 +59,8 @@ width: 36px; } - button.icon-button iron-icon { - height: 20px; - width: 20px; - } - button.more-vert-button { - height: 36px; padding: 8px; - width: 36px; } button.more-vert-button div { diff --git a/chromium/chrome/browser/resources/md_history/shared_vars.html b/chromium/chrome/browser/resources/md_history/shared_vars.html index 2be07afcf76..eff885fafd6 100644 --- a/chromium/chrome/browser/resources/md_history/shared_vars.html +++ b/chromium/chrome/browser/resources/md_history/shared_vars.html @@ -20,6 +20,8 @@ --history-item-time-color: #646464; --interactive-color: var(--google-blue-500); --item-height: 44px; + --iron-icon-height: var(--cr-icon-size); + --iron-icon-width: var(--cr-icon-size); --link-color: var(--google-blue-700); --primary-text-color: var(--paper-grey-900); --secondary-text-color: var(--paper-grey-600); diff --git a/chromium/chrome/browser/resources/md_history/side_bar.html b/chromium/chrome/browser/resources/md_history/side_bar.html index 8ea9c0262e4..b49c3e2fe5c 100644 --- a/chromium/chrome/browser/resources/md_history/side_bar.html +++ b/chromium/chrome/browser/resources/md_history/side_bar.html @@ -84,7 +84,6 @@ #footer-text { -webkit-padding-end: 16px; -webkit-padding-start: 24px; - line-height: 20px; margin: 24px 0; } diff --git a/chromium/chrome/browser/resources/md_history/synced_device_card.html b/chromium/chrome/browser/resources/md_history/synced_device_card.html index 9e80d9b8083..708f60bcf3c 100644 --- a/chromium/chrome/browser/resources/md_history/synced_device_card.html +++ b/chromium/chrome/browser/resources/md_history/synced_device_card.html @@ -48,14 +48,10 @@ } #right-buttons { - -webkit-margin-end: 4px; + -webkit-margin-end: 12px; color: var(--secondary-text-color); } - #menu-button { - -webkit-margin-end: 8px; - } - #collapse { overflow: hidden; } @@ -97,7 +93,7 @@ <div></div> <div></div> </button> - <button is="paper-icon-button-light" class="icon-button" + <button is="paper-icon-button-light" id="collapse-button" title$="[[getCollapseTitle_(opened)]]"> <iron-icon icon="[[getCollapseIcon_(opened)]]" id="dropdown-indicator"> diff --git a/chromium/chrome/browser/resources/md_user_manager/create_profile.html b/chromium/chrome/browser/resources/md_user_manager/create_profile.html index a14ae723f83..ef416b56a65 100644 --- a/chromium/chrome/browser/resources/md_user_manager/create_profile.html +++ b/chromium/chrome/browser/resources/md_user_manager/create_profile.html @@ -22,6 +22,10 @@ <template> <style include="shared-styles iron-positioning md-select paper-checkbox-style"> + :host { + align-self: center; + } + .container { color: var(--primary-text-color); width: var(--page-width); @@ -67,10 +71,8 @@ } #title-bar { - border-bottom: var(--user-manager-separator-line); font-size: 16px; font-weight: 500; - padding: 104px 0 16px; } #nameInput { diff --git a/chromium/chrome/browser/resources/md_user_manager/icons.html b/chromium/chrome/browser/resources/md_user_manager/icons.html deleted file mode 100644 index 2143f12a3a9..00000000000 --- a/chromium/chrome/browser/resources/md_user_manager/icons.html +++ /dev/null @@ -1,14 +0,0 @@ -<link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html"> - -<iron-iconset-svg name="user-manager" size="24"> - <svg> - <defs> - <!-- - These icons are copied from Polymer's iron-icons and kept in sorted order. - See http://goo.gl/Y1OdAq for instructions on adding additional icons. - --> - <g id="supervisor-account"><path d="M16.5 12c1.38 0 2.49-1.12 2.49-2.5S17.88 7 16.5 7C15.12 7 14 8.12 14 9.5s1.12 2.5 2.5 2.5zM9 11c1.66 0 2.99-1.34 2.99-3S10.66 5 9 5C7.34 5 6 6.34 6 8s1.34 3 3 3zm7.5 3c-1.83 0-5.5.92-5.5 2.75V19h11v-2.25c0-1.83-3.67-2.75-5.5-2.75zM9 13c-2.33 0-7 1.17-7 3.5V19h7v-2.25c0-.85.33-2.34 2.37-3.47C10.5 13.1 9.66 13 9 13z"></path></g> - </defs> - </svg> -</iron-iconset-svg> diff --git a/chromium/chrome/browser/resources/md_user_manager/supervised_user_create_confirm.html b/chromium/chrome/browser/resources/md_user_manager/supervised_user_create_confirm.html index 4b50826658d..f87044917b8 100644 --- a/chromium/chrome/browser/resources/md_user_manager/supervised_user_create_confirm.html +++ b/chromium/chrome/browser/resources/md_user_manager/supervised_user_create_confirm.html @@ -1,6 +1,5 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="/icons.html"> <link rel="import" href="/profile_browser_proxy.html"> <link rel="import" href="/shared_styles.html"> <link rel="import" href="chrome://resources/html/util.html"> @@ -21,15 +20,8 @@ } #title-area { - border-bottom: var(--user-manager-separator-line); font-size: 16px; - padding-bottom: 16px; - } - - #title-area iron-icon { - --iron-icon-height: 20px; - --iron-icon-width: 20px; - color: var(--title-icon-color); + font-weight: 500; } .content-area { @@ -52,7 +44,6 @@ <div id="container"> <div id="title-area" class="horizontal justified layout"> <span id="title">[[titleText_(profileInfo)]]</span> - <iron-icon icon="user-manager:supervisor-account"></iron-icon> </div> <div class="content-area" inner-h-t-m-l="[[confirmationMessage_(profileInfo)]]"> diff --git a/chromium/chrome/browser/resources/md_user_manager/supervised_user_learn_more.html b/chromium/chrome/browser/resources/md_user_manager/supervised_user_learn_more.html index 2547500b9ab..5ada724a33f 100644 --- a/chromium/chrome/browser/resources/md_user_manager/supervised_user_learn_more.html +++ b/chromium/chrome/browser/resources/md_user_manager/supervised_user_learn_more.html @@ -1,6 +1,5 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="/icons.html"> <link rel="import" href="/shared_styles.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"> @@ -19,15 +18,8 @@ } #title-area { - border-bottom: var(--user-manager-separator-line); font-size: 16px; - padding-bottom: 16px; - } - - #title-area iron-icon { - --iron-icon-height: 20px; - --iron-icon-width: 20px; - color: var(--title-icon-color); + font-weight: 500; } .content-area { @@ -50,7 +42,6 @@ <div id="container"> <div id="title-area" class="horizontal justified layout"> <span id="title">$i18n{supervisedUserLearnMoreTitle}</span> - <iron-icon icon="user-manager:supervisor-account"></iron-icon> </div> <div class="content-area">$i18nRaw{supervisedUserLearnMoreText}</div> <div id="actions"> diff --git a/chromium/chrome/browser/resources/md_user_manager/user_manager.html b/chromium/chrome/browser/resources/md_user_manager/user_manager.html index 95537c923d2..1d493a4e909 100644 --- a/chromium/chrome/browser/resources/md_user_manager/user_manager.html +++ b/chromium/chrome/browser/resources/md_user_manager/user_manager.html @@ -72,6 +72,17 @@ opacity: 0; } + /* The name-container font-sizes is to counteract the countainer's + * scale(0.8), so that the text still stays legible. */ + podrow[ncolumns='6'] .pod .name-container { + font-size: 1.25em; + transition: font-size 180ms; + } + + podrow[ncolumns='6'] .pod.focused .name-container { + font-size: 1em; + } + .pod { box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .24), 0 0 2px 0 rgba(0, 0, 0, .12); @@ -289,6 +300,10 @@ padding: 12px; } + .action-box-remove-user-warning-text { + max-width: 100%; + } + .action-box-remove-user-warning > * { word-wrap: break-word; } diff --git a/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.html b/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.html index 10f655aa774..8a4acecdaf6 100644 --- a/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.html +++ b/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.html @@ -28,7 +28,7 @@ <neon-animated-pages id="animatedPages" attr-for-selected="id" selected="[[selectedPage_]]" entry-animation="fade-in-animation" exit-animation="fade-out-animation"> - <neon-animatable id="create-user-page"> + <neon-animatable id="create-user-page" on-keydown="stopPropagation_"> <!-- Keep the page alive while visiting the Learn More page. --> <template is="dom-if" if="[[isPresentIn_(selectedPage_, @@ -40,13 +40,15 @@ <neon-animatable id="user-pods-page"> <slot></slot> </neon-animatable> - <neon-animatable id="supervised-learn-more-page"> + <neon-animatable id="supervised-learn-more-page" + on-keydown="stopPropagation_"> <template is="dom-if" if="[[isPresentIn_(selectedPage_, 'supervised-learn-more-page')]]"> <supervised-user-learn-more></supervised-user-learn-more> </template> </neon-animatable> - <neon-animatable id="supervised-create-confirm-page"> + <neon-animatable id="supervised-create-confirm-page" + on-keydown="stopPropagation_"> <template is="dom-if" if="[[isPresentIn_(selectedPage_, 'supervised-create-confirm-page')]]"> diff --git a/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.js b/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.js index 39b932fd8c0..a579c3d540d 100644 --- a/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.js +++ b/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.js @@ -46,6 +46,20 @@ Polymer({ }, /** + * This is to prevent events from propagating to the document element, which + * erroneously triggers user-pod selections. + * + * TODO(scottchen): re-examine if its necessary for user_pod_row.js to bind + * listeners on the entire document element. + * + * @param {!Event} e + * @private + */ + stopPropagation_: function(e) { + e.stopPropagation(); + }, + + /** * Returns True if the first argument is present in the given set of values. * @param {string} selectedPage ID of the currently selected page. * @param {...string} var_args Pages IDs to check the first argument against. diff --git a/chromium/chrome/browser/resources/media/media_engagement.html b/chromium/chrome/browser/resources/media/media_engagement.html index d789f520375..fd374ce52d3 100644 --- a/chromium/chrome/browser/resources/media/media_engagement.html +++ b/chromium/chrome/browser/resources/media/media_engagement.html @@ -20,6 +20,7 @@ table { border-collapse: collapse; + margin-bottom: 20px; } table td, @@ -54,7 +55,9 @@ .visits-count-cell, .media-playbacks-count-cell, - .last-playback-time-cell { + .last-playback-time-cell, + .audible-playbacks-count-cell, + .significant-playbacks-count-cell { background-color: rgba(230, 230, 230, 0.5); text-align: right; } @@ -72,6 +75,10 @@ outline: none; } + .name-cell { + background-color: rgba(230, 230, 230, 0.5); + } + table tr:hover { background: rgb(255, 255, 187); } @@ -91,6 +98,20 @@ <h1>Media Engagement</h1> <table> <thead> + <tr id="config-table-header"> + <th> + Setting Name + </th> + <th> + Setting Value + </th> + </tr> + </thead> + <tbody id="config-table-body"> + </tbody> + </table> + <table> + <thead> <tr id="engagement-table-header"> <th sort-key="origin"> Origin @@ -99,7 +120,13 @@ Visits </th> <th sort-key="mediaPlaybacks" sort-reverse> - Number of Playbacks + Playbacks + </th> + <th sort-key="audiblePlaybacks" sort-reverse> + Audible Playbacks* + </th> + <th sort-key="significantPlaybacks" sort-reverse> + Significant Playbacks* </th> <th sort-key="lastMediaPlaybackTime" sort-reverse> Last Playback @@ -116,11 +143,17 @@ </tbody> </table> + <p> + * These columns are experimental and do not currently affect the MEI score. + </p> + <template id="datarow"> <tr> <td class="origin-cell"></td> <td class="visits-count-cell"></td> <td class="media-playbacks-count-cell"></td> + <td class="audible-playbacks-count-cell"></td> + <td class="significant-playbacks-count-cell"></td> <td class="last-playback-time-cell"></td> <td class="is-high-cell"></td> <td class="total-score-cell"></td> @@ -129,5 +162,11 @@ </td> </tr> </template> + <template id="configrow"> + <tr> + <td class="name-cell"></td> + <td></td> + </tr> + </template> </body> </html> diff --git a/chromium/chrome/browser/resources/media/media_engagement.js b/chromium/chrome/browser/resources/media/media_engagement.js index 268f7b1ca2c..0ca0a3cd94c 100644 --- a/chromium/chrome/browser/resources/media/media_engagement.js +++ b/chromium/chrome/browser/resources/media/media_engagement.js @@ -18,6 +18,7 @@ var info = null; var engagementTableBody = null; var sortReverse = true; var sortKey = 'totalScore'; +var configTableBody = null; /** * Creates a single row in the engagement table. @@ -30,12 +31,14 @@ function createRow(rowInfo) { td[0].textContent = rowInfo.origin.url; td[1].textContent = rowInfo.visits; td[2].textContent = rowInfo.mediaPlaybacks; - td[3].textContent = rowInfo.lastMediaPlaybackTime ? + td[3].textContent = rowInfo.audiblePlaybacks; + td[4].textContent = rowInfo.significantPlaybacks; + td[5].textContent = rowInfo.lastMediaPlaybackTime ? new Date(rowInfo.lastMediaPlaybackTime).toISOString() : ''; - td[4].textContent = rowInfo.isHigh ? 'Yes' : 'No'; - td[5].textContent = rowInfo.totalScore ? rowInfo.totalScore.toFixed(2) : '0'; - td[6].getElementsByClassName('engagement-bar')[0].style.width = + td[6].textContent = rowInfo.isHigh ? 'Yes' : 'No'; + td[7].textContent = rowInfo.totalScore ? rowInfo.totalScore.toFixed(2) : '0'; + td[8].getElementsByClassName('engagement-bar')[0].style.width = (rowInfo.totalScore * 50) + 'px'; return document.importNode(template.content, true); } @@ -73,7 +76,8 @@ function compareTableItem(sortKey, a, b) { return new URL(val1.url).host > new URL(val2.url).host ? 1 : -1; if (sortKey == 'visits' || sortKey == 'mediaPlaybacks' || - sortKey == 'lastMediaPlaybackTime' || sortKey == 'totalScore') { + sortKey == 'lastMediaPlaybackTime' || sortKey == 'totalScore' || + sortKey == 'audiblePlaybacks' || sortKey == 'significantPlaybacks') { return val1 - val2; } @@ -82,6 +86,36 @@ function compareTableItem(sortKey, a, b) { } /** + * Creates a single row in the config table. + * @param {string} name The name of the config setting. + * @param {string} value The value of the config setting. + * @return {!HTMLElement} + */ +function createConfigRow(name, value) { + var template = $('configrow'); + var td = template.content.querySelectorAll('td'); + td[0].textContent = name; + td[1].textContent = value; + return document.importNode(template.content, true); +} + +/** + * Regenerates the config table. + * @param {!MediaEngagementConfig} config The config of the MEI service. + */ + +function renderConfigTable(config) { + configTableBody.innerHTML = ''; + + configTableBody.appendChild( + createConfigRow('Min Visits', config.scoreMinVisits)); + configTableBody.appendChild( + createConfigRow('Lower Threshold', config.highScoreLowerThreshold)); + configTableBody.appendChild( + createConfigRow('Upper Threshold', config.highScoreUpperThreshold)); +} + +/** * Regenerates the engagement table from |info|. */ function renderTable() { @@ -100,6 +134,11 @@ function updateEngagementTable() { renderTable(); pageIsPopulatedResolver.resolve(); }); + + // Populate config settings. + uiHandler.getMediaEngagementConfig().then(response => { + renderConfigTable(response.config); + }); } document.addEventListener('DOMContentLoaded', function() { @@ -110,6 +149,7 @@ document.addEventListener('DOMContentLoaded', function() { updateEngagementTable(); engagementTableBody = $('engagement-table-body'); + configTableBody = $('config-table-body'); // Set table header sort handlers. var engagementTableHeader = $('engagement-table-header'); diff --git a/chromium/chrome/browser/resources/media_router/elements/issue_banner/issue_banner.html b/chromium/chrome/browser/resources/media_router/elements/issue_banner/issue_banner.html index 1087a9ae8db..eb9c718b797 100644 --- a/chromium/chrome/browser/resources/media_router/elements/issue_banner/issue_banner.html +++ b/chromium/chrome/browser/resources/media_router/elements/issue_banner/issue_banner.html @@ -1,7 +1,7 @@ +<link rel="import" href="chrome://resources/cr_elements/icons.html"> <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/paper-button/paper-button.html"> -<link rel="import" href="../../icons/media_router_icons.html"> <dom-module id="issue-banner"> <link rel="import" type="css" href="../../media_router_common.css"> <link rel="import" type="css" href="issue_banner.css"> @@ -9,7 +9,7 @@ <div class$="[[computeIssueClass_(issue)]]"> <div> <div hidden$="[[computeIsBlockingIssueHidden_(issue)]]"> - <iron-icon icon="media-router:error-outline" id="blocking-icon"> + <iron-icon icon="cr:error-outline" id="blocking-icon"> </iron-icon> </div> <div id="title" aria-live="polite" tabindex="0">[[issue.title]]</div> 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 3b9b0631cc1..ed76cd9ed9b 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 @@ -16,13 +16,6 @@ right: 20px; } -.ellipsis { - padding: 0 1%; - text-overflow: ellipsis; - white-space: nowrap; - width: 90%; -} - :host-context([dir='rtl']) #play-pause-volume-hangouts-controls { transform: scaleX(-1); } @@ -48,7 +41,7 @@ #route-description { margin: 15px 8px 3px 8px; - overflow: hidden; + width: 90%; } #route-time-controls { @@ -87,19 +80,19 @@ padding: 0.3em 0; } +paper-checkbox { + --paper-checkbox-checked-color: #1976D2; +} + #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; }; @@ -109,3 +102,15 @@ margin-top: 2px; width: 249px; } + +#mirroring-fullscreen-video-controls { + display: inline-block; + font-size: 0.8em; + margin: 15px 8px 3px 8px; + vertical-align: middle; + white-space: nowrap; +} + +#mirroring-fullscreen-video-dropdown { + width: auto; +} 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 7a7ac5df9d9..84bf30214b8 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,3 +1,4 @@ +<link rel="import" href="chrome://resources/html/md_select_css.html"> <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"> @@ -7,10 +8,16 @@ <link rel="import" type="css" href="../../media_router_common.css"> <link rel="import" type="css" href="route_controls.css"> <template> + <style include="md-select"></style> <div id="media-controls"> + <!-- + TODO(crbug.com/786208): Remove the div below and always render the + description in the details element. And, possibly combine details and + controls elements. + --> <div class="ellipsis" id="route-description" - title="[[displayedDescription_]]"> - [[displayedDescription_]] + title="[[routeDescription_]]"> + [[routeDescription_]] </div> <div class="ellipsis" id="route-title" title="[[routeStatus.title]]"> [[routeStatus.title]] @@ -81,6 +88,23 @@ </paper-checkbox> </div> </div> + <div id="mirroring-fullscreen-video-controls" + hidden="[[!routeStatus.mirroringExtraData]]"> + [[i18n('fullscreenVideosDropdownTitle')]] + <span class="md-select-wrapper"> + <select class="md-select" + id="mirroring-fullscreen-video-dropdown" + on-change="onFullscreenVideoDropdownChange_"> + <option value="[[FullscreenVideoOption_.REMOTE_SCREEN]]"> + [[i18n('fullscreenVideosRemoteScreen')]] + </option> + <option value="[[FullscreenVideoOption_.BOTH_SCREENS]]"> + [[i18n('fullscreenVideosBothScreens')]] + </option> + </select> + <span class="md-select-underline"></span> + </span> + </div> </div> </div> </template> 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 00ec83a1893..570f03fe0e8 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 @@ -12,6 +12,20 @@ Polymer({ properties: { /** + * Set of possible options for playing fullscreen videos when mirroring. + * @private {!Object} + */ + FullscreenVideoOption_: { + type: Object, + value: { + // Play on remote screen only. + REMOTE_SCREEN: 'remote_screen', + // Play on both remote and local screens. + BOTH_SCREENS: 'both_screens' + } + }, + + /** * The current time displayed in seconds, before formatting. * @private {number} */ @@ -21,11 +35,11 @@ Polymer({ }, /** - * The media description to display. Uses route description if none is - * provided by the route status object. + * The route description to display. Uses the media route description if + * none is provided by the media route status object. * @private {string} */ - displayedDescription_: { + routeDescription_: { type: String, value: '', }, @@ -50,6 +64,15 @@ Polymer({ }, /** + * Keep in sync with media remoting individual user setting. + * @private {boolean} + */ + mediaRemotingEnabled_: { + type: Boolean, + value: true, + }, + + /** * The timestamp for when the initial media status was loaded. * @private {number} */ @@ -348,7 +371,7 @@ Polymer({ this.displayedVolume_ = Math.round(newRouteStatus.volume * 100) / 100; } if (newRouteStatus.description !== '') { - this.displayedDescription_ = newRouteStatus.description; + this.routeDescription_ = newRouteStatus.description; } if (!this.initialLoadTime_) { this.initialLoadTime_ = Date.now(); @@ -362,6 +385,16 @@ Polymer({ } this.hangoutsLocalPresent_ = !!newRouteStatus.hangoutsExtraData && newRouteStatus.hangoutsExtraData.localPresent; + if (newRouteStatus.mirroringExtraData) { + // Manually update the selected value on the + // mirroring-fullscreen-video-dropdown dropbox. + // TODO(imcheng): Avoid doing this by wrapping the dropbox in a Polymer + // template, or introduce <paper-dropdown-menu> to the Polymer library. + this.$['mirroring-fullscreen-video-dropdown'].value = + newRouteStatus.mirroringExtraData.mediaRemotingEnabled ? + this.FullscreenVideoOption_.REMOTE_SCREEN : + this.FullscreenVideoOption_.BOTH_SCREENS; + } }, /** @@ -375,8 +408,7 @@ Polymer({ this.stopIncrementingCurrentTime_(); } if (route && this.routeStatus.description === '') { - this.displayedDescription_ = - loadTimeData.getStringF('castingActivityStatus', route.description); + this.routeDescription_ = route.description; } }, @@ -436,6 +468,19 @@ Polymer({ }, /** + * Called when the value on the mirroring-fullscreen-video-dropdown dropdown + * menu changes. + * @param {!Event} e + * @private + */ + onFullscreenVideoDropdownChange_: function(e) { + /** @const */ var dropdownValue = + this.$['mirroring-fullscreen-video-dropdown'].value; + media_router.browserApi.setMediaRemotingEnabled( + dropdownValue == this.FullscreenVideoOption_.REMOTE_SCREEN); + }, + + /** * Resets the route controls. Called when the route details view is closed. */ reset: function() { diff --git a/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.css b/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.css index 742647da622..92f8b1a7dd1 100644 --- a/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.css +++ b/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.css @@ -17,12 +17,10 @@ text-align: end; } -#route-information { +#route-description { -webkit-padding-end: var(--dialog-padding-end); -webkit-padding-start: 44px; - background-color: white; font-size: 1.2em; line-height: 1.5em; margin-top: 16px; - overflow: hidden; } 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 6bc2093cb04..8d91b1beac5 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 @@ -7,9 +7,9 @@ <link rel="import" type="css" href="../../media_router_common.css"> <link rel="import" type="css" href="route_details.css"> <template> - <div id="route-information" - hidden$="[[!shouldShowRouteInfoOnly_(controllerType_)]]"> - <span>[[activityStatus_]]</span> + <div class="ellipsis" id="route-description" title="[[routeDescription_]]" + hidden$="[[!shouldShowRouteDescription_(controllerType_)]]"> + [[routeDescription_]] </div> <template is="dom-if" if="[[shouldAttemptLoadingExtensionView_(route)]]"> <extension-view-wrapper id="extension-view-wrapper" route="[[route]]" 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 b260d818c80..8724cd240f7 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 @@ -9,10 +9,10 @@ Polymer({ properties: { /** - * The text for the current casting activity status. + * Description of the current casting activity, e.g. "Casting YouTube". * @private {string|undefined} */ - activityStatus_: { + routeDescription_: { type: String, }, @@ -191,15 +191,12 @@ Polymer({ }, /** - * Updates |activityStatus_| for the default view. + * Updates |routeDescription_| for the default view. * * @private */ - updateActivityStatus_: function() { - this.activityStatus_ = this.route ? - loadTimeData.getStringF( - 'castingActivityStatus', this.route.description) : - ''; + updateRouteDescription_: function() { + this.routeDescription_ = this.route ? this.route.description : ''; }, /** @@ -231,7 +228,7 @@ Polymer({ */ onRouteChange_: function(newRoute) { if (this.controllerType_ !== media_router.ControllerType.WEBUI) { - this.updateActivityStatus_(); + this.updateRouteDescription_(); } }, @@ -260,7 +257,7 @@ Polymer({ * the extensionview or the WebUI route controller. * @private */ - shouldShowRouteInfoOnly_: function(controllerType) { + shouldShowRouteDescription_: function(controllerType) { return controllerType === media_router.ControllerType.NONE; }, diff --git a/chromium/chrome/browser/resources/media_router/icons/media_router_icons.html b/chromium/chrome/browser/resources/media_router/icons/media_router_icons.html index 5cf41b8e90d..758151b69db 100644 --- a/chromium/chrome/browser/resources/media_router/icons/media_router_icons.html +++ b/chromium/chrome/browser/resources/media_router/icons/media_router_icons.html @@ -10,7 +10,6 @@ <g id="arrow-drop-up"><path d="M7 14l5-5 5 5z"></path></g> <g id="arrow-forward"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"></path></g> <g id="close"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path></g> - <g id="error-outline"><path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"></path></g> <g id="folder"><path d="M0 0h24v24H0z" fill="none"></path><path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"></path></g> <g id="search"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></g> <g id="tab"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h10v4h8v10z"></path></g> diff --git a/chromium/chrome/browser/resources/media_router/media_router.css b/chromium/chrome/browser/resources/media_router/media_router.css index ad517c4ccee..4b3904a5add 100644 --- a/chromium/chrome/browser/resources/media_router/media_router.css +++ b/chromium/chrome/browser/resources/media_router/media_router.css @@ -3,7 +3,6 @@ * found in the LICENSE file. */ body { - font-family: Roboto; font-size: 0.75em; margin: 0; } 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 fe5e3b12e0e..9b5724b7401 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 @@ -312,6 +312,15 @@ cr.define('media_router.browserApi', function() { chrome.send('hangouts.setLocalPresent', [localPresent]); } + /** + * Sends a command to change the Media Remoting enabled value associated with + * current route. + * @param {boolean} enabled + */ + function setMediaRemotingEnabled(enabled) { + chrome.send('setMediaRemotingEnabled', [enabled]); + } + return { acknowledgeFirstRunFlow: acknowledgeFirstRunFlow, actOnIssue: actOnIssue, @@ -344,6 +353,7 @@ cr.define('media_router.browserApi', function() { selectLocalMediaFile: selectLocalMediaFile, setCurrentMediaMute: setCurrentMediaMute, setCurrentMediaVolume: setCurrentMediaVolume, - setHangoutsLocalPresent: setHangoutsLocalPresent + setHangoutsLocalPresent: setHangoutsLocalPresent, + setMediaRemotingEnabled: setMediaRemotingEnabled }; }); diff --git a/chromium/chrome/browser/resources/media_router/media_router_common.css b/chromium/chrome/browser/resources/media_router/media_router_common.css index dd22f1cc105..941120df475 100644 --- a/chromium/chrome/browser/resources/media_router/media_router_common.css +++ b/chromium/chrome/browser/resources/media_router/media_router_common.css @@ -8,6 +8,9 @@ --dialog-width: 340px; --navigation-icon-button-size: 36px; --non-navigation-icon-size: 16px; + -webkit-font-smoothing: antialiased; + -webkit-tap-highlight-color: transparent; + font-family: 'Roboto', 'Noto', sans-serif; } .button { @@ -19,3 +22,14 @@ [hidden] { display: none !important; } + +.ellipsis { + padding: 0 1%; + text-overflow: ellipsis; + white-space: nowrap; +} + +#route-description { + background-color: white; + overflow: hidden; +} 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 d8252460856..bcb1b1664be 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 {!{mediaRemotingEnabled: boolean}=} mirroringExtraData Only set for + * mirroring routes. * @param {!{localPresent: boolean}=} hangoutsExtraData Only set for Hangouts * routes. * @constructor @@ -260,7 +262,7 @@ cr.define('media_router', function() { canSetVolume = false, canSeek = false, playState = media_router.PlayState.PLAYING, isPaused = false, isMuted = false, volume = 0, duration = 0, currentTime = 0, - hangoutsExtraData = undefined) { + hangoutsExtraData = undefined, mirroringExtraData = undefined) { /** @type {string} */ this.title = title; @@ -297,6 +299,9 @@ cr.define('media_router', function() { /** @type {!{localPresent: boolean}|undefined} */ this.hangoutsExtraData = hangoutsExtraData; + + /** @type {!{mediaRemotingEnabled: boolean}|undefined} */ + this.mirroringExtraData = mirroringExtraData; }; /** diff --git a/chromium/chrome/browser/resources/net_internals/browser_bridge.js b/chromium/chrome/browser/resources/net_internals/browser_bridge.js index 3068a34fe22..9a9c1c9e284 100644 --- a/chromium/chrome/browser/resources/net_internals/browser_bridge.js +++ b/chromium/chrome/browser/resources/net_internals/browser_bridge.js @@ -184,6 +184,10 @@ var BrowserBridge = (function() { this.send('expectCTAdd', [domain, report_uri, enforce]); }, + sendExpectCTTestReport: function(report_uri) { + this.send('expectCTTestReport', [report_uri]); + }, + sendGetSessionNetworkStats: function() { this.send('getSessionNetworkStats'); }, @@ -331,6 +335,11 @@ var BrowserBridge = (function() { this.expectCTObservers_[i].onExpectCTQueryResult(info); }, + receivedExpectCTTestReportResult: function(result) { + for (var i = 0; i < this.expectCTObservers_.length; i++) + this.expectCTObservers_[i].onExpectCTTestReportResult(result); + }, + receivedONCFileParse: function(error) { for (var i = 0; i < this.crosONCFileParseObservers_.length; i++) this.crosONCFileParseObservers_[i].onONCFileParse(error); diff --git a/chromium/chrome/browser/resources/net_internals/domain_security_policy_view.html b/chromium/chrome/browser/resources/net_internals/domain_security_policy_view.html index ec6f6a25aed..5eb28dce892 100644 --- a/chromium/chrome/browser/resources/net_internals/domain_security_policy_view.html +++ b/chromium/chrome/browser/resources/net_internals/domain_security_policy_view.html @@ -79,6 +79,19 @@ id=expect-ct-view-query-output> </div> + <h4>Send test Expect-CT report</h4> + + <p>Trigger a test report to the given report URI. The report will contain a + hostname of "expect-ct-report.test" and dummy data in other fields.</p> + <form id=expect-ct-view-test-report-form> + <label>Report URI: <input type=text id=expect-ct-view-test-report-uri + type="url"></label> + <input type=submit value="Send" id=expect-ct-view-test-report-submit> + </form> + <div style="margin-top: 1em; margin-left: 2em;" + id=expect-ct-view-test-report-output> + </div> + <h3>Delete domain security policies</h3> <p> diff --git a/chromium/chrome/browser/resources/net_internals/domain_security_policy_view.js b/chromium/chrome/browser/resources/net_internals/domain_security_policy_view.js index b345ad282bb..6f0d5a26b17 100644 --- a/chromium/chrome/browser/resources/net_internals/domain_security_policy_view.js +++ b/chromium/chrome/browser/resources/net_internals/domain_security_policy_view.js @@ -48,6 +48,10 @@ var DomainSecurityPolicyView = (function() { $(DomainSecurityPolicyView.QUERY_EXPECT_CT_INPUT_ID); this.queryExpectCTOutputDiv_ = $(DomainSecurityPolicyView.QUERY_EXPECT_CT_OUTPUT_DIV_ID); + this.testExpectCTReportInput_ = + $(DomainSecurityPolicyView.TEST_REPORT_EXPECT_CT_INPUT_ID); + this.testExpectCTOutputDiv_ = + $(DomainSecurityPolicyView.TEST_REPORT_EXPECT_CT_OUTPUT_DIV_ID); var form = $(DomainSecurityPolicyView.DELETE_FORM_ID); form.addEventListener('submit', this.onSubmitDelete_.bind(this), false); @@ -59,7 +63,7 @@ var DomainSecurityPolicyView = (function() { form.addEventListener( 'submit', this.onSubmitHSTSPKPQuery_.bind(this), false); - var form = $(DomainSecurityPolicyView.ADD_EXPECT_CT_FORM_ID); + form = $(DomainSecurityPolicyView.ADD_EXPECT_CT_FORM_ID); form.addEventListener( 'submit', this.onSubmitExpectCTAdd_.bind(this), false); @@ -67,6 +71,10 @@ var DomainSecurityPolicyView = (function() { form.addEventListener( 'submit', this.onSubmitExpectCTQuery_.bind(this), false); + form = $(DomainSecurityPolicyView.TEST_REPORT_EXPECT_CT_FORM_ID); + form.addEventListener( + 'submit', this.onSubmitExpectCTTestReport_.bind(this), false); + g_browser.addHSTSObserver(this); g_browser.addExpectCTObserver(this); } @@ -116,6 +124,14 @@ var DomainSecurityPolicyView = (function() { 'expect-ct-view-query-submit'; DomainSecurityPolicyView.QUERY_EXPECT_CT_OUTPUT_DIV_ID = 'expect-ct-view-query-output'; + DomainSecurityPolicyView.TEST_REPORT_EXPECT_CT_INPUT_ID = + 'expect-ct-view-test-report-uri'; + DomainSecurityPolicyView.TEST_REPORT_EXPECT_CT_FORM_ID = + 'expect-ct-view-test-report-form'; + DomainSecurityPolicyView.TEST_REPORT_EXPECT_CT_SUBMIT_ID = + 'expect-ct-view-test-report-submit'; + DomainSecurityPolicyView.TEST_REPORT_EXPECT_CT_OUTPUT_DIV_ID = + 'expect-ct-view-test-report-output'; cr.addSingletonGetter(DomainSecurityPolicyView); @@ -285,7 +301,21 @@ var DomainSecurityPolicyView = (function() { } yellowFade(this.queryExpectCTOutputDiv_); - } + }, + + onSubmitExpectCTTestReport_: function(event) { + g_browser.sendExpectCTTestReport(this.testExpectCTReportInput_.value); + event.preventDefault(); + }, + + onExpectCTTestReportResult: function(result) { + if (result == 'success') { + addTextNode(this.testExpectCTOutputDiv_, 'Test report succeeded'); + } else { + addTextNode(this.testExpectCTOutputDiv_, 'Test report failed'); + } + yellowFade(this.testExpectCTOutputDiv_); + }, }; diff --git a/chromium/chrome/browser/resources/net_internals/events_view.js b/chromium/chrome/browser/resources/net_internals/events_view.js index 679fdbc3fc4..60cd5a8134e 100644 --- a/chromium/chrome/browser/resources/net_internals/events_view.js +++ b/chromium/chrome/browser/resources/net_internals/events_view.js @@ -144,13 +144,6 @@ var EventsView = (function() { }, /** - * Updates text in the details view when privacy stripping is toggled. - */ - onPrivacyStrippingChanged: function() { - this.invalidateDetailsView_(); - }, - - /** * Updates text in the details view when time display mode is toggled. */ onUseRelativeTimesChanged: function() { diff --git a/chromium/chrome/browser/resources/net_internals/log_util.js b/chromium/chrome/browser/resources/net_internals/log_util.js index f690352a5ca..b616f2e1da3 100644 --- a/chromium/chrome/browser/resources/net_internals/log_util.js +++ b/chromium/chrome/browser/resources/net_internals/log_util.js @@ -9,8 +9,7 @@ log_util = (function() { * Creates a new log dump. |events| is a list of all events, |polledData| is * an object containing the results of each poll, |tabData| is an object * containing data for individual tabs, |date| is the time the dump was - * created, as a formatted string, and |privacyStripping| is whether or not - * private information should be removed from the generated dump. + * created, as a formatted string. * * Returns the new log dump as an object. Resulting object may have a null * |numericDate|. @@ -32,11 +31,7 @@ log_util = (function() { * tabs not present on the OS the log is from. */ function createLogDump( - userComments, constants, events, polledData, tabData, numericDate, - privacyStripping) { - if (privacyStripping) - events = events.map(stripPrivacyInfo); - + userComments, constants, events, polledData, tabData, numericDate) { var logDump = { 'userComments': userComments, 'constants': constants, @@ -57,12 +52,11 @@ log_util = (function() { * Creates a full log dump using |polledData| and the return value of each * tab's saveState function and passes it to |callback|. */ - function onUpdateAllCompleted( - userComments, callback, privacyStripping, polledData) { + function onUpdateAllCompleted(userComments, callback, polledData) { var logDump = createLogDump( userComments, Constants, EventsTracker.getInstance().getAllCapturedEvents(), polledData, - getTabData_(), timeutil.getCurrentTime(), privacyStripping); + getTabData_(), timeutil.getCurrentTime()); callback(JSON.stringify(logDump)); } @@ -71,9 +65,9 @@ log_util = (function() { * loaded. Once a log dump has been created, |callback| is passed the dumped * text as a string. */ - function createLogDumpAsync(userComments, callback, privacyStripping) { - g_browser.updateAllInfo(onUpdateAllCompleted.bind( - null, userComments, callback, privacyStripping)); + function createLogDumpAsync(userComments, callback) { + g_browser.updateAllInfo( + onUpdateAllCompleted.bind(null, userComments, callback)); } /** 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 edb063bed16..205fcfae847 100644 --- a/chromium/chrome/browser/resources/net_internals/log_view_painter.js +++ b/chromium/chrome/browser/resources/net_internals/log_view_painter.js @@ -6,7 +6,6 @@ var createLogEntryTablePrinter; var proxySettingsToString; -var stripPrivacyInfo; // Start of anonymous namespace. (function() { @@ -22,8 +21,7 @@ function canCollapseBeginWithEnd(beginEntry) { * Creates a TablePrinter for use by the above two functions. baseTime is * the time relative to which other times are displayed. */ -createLogEntryTablePrinter = function( - logEntries, privacyStripping, baseTime, logCreationTime) { +createLogEntryTablePrinter = function(logEntries, baseTime, logCreationTime) { var entries = LogGroupEntry.createArrayFrom(logEntries); var tablePrinter = new TablePrinter(); var parameterOutputter = new ParameterOutputter(tablePrinter); @@ -66,7 +64,7 @@ createLogEntryTablePrinter = function( if (typeof entry.orig.params == 'object') { // Those 5 skipped cells are: two for "t=", and three for "st=". tablePrinter.setNewRowCellIndent(5 + entry.getDepth()); - writeParameters(entry.orig, privacyStripping, parameterOutputter); + writeParameters(entry.orig, parameterOutputter); tablePrinter.setNewRowCellIndent(0); } @@ -240,15 +238,10 @@ var ParameterOutputter = (function() { * Certain event types have custom pretty printers. Everything else will * default to a JSON-like format. */ -function writeParameters(entry, privacyStripping, out) { - if (privacyStripping) { - // If privacy stripping is enabled, remove data as needed. - entry = stripPrivacyInfo(entry); - } else { - // If headers are in an object, convert them to an array for better - // display. - entry = reformatHeaders(entry); - } +function writeParameters(entry, out) { + // If headers are in an object, convert them to an array for better + // display. + entry = reformatHeaders(entry); // Use any parameter writer available for this event type. var paramsWriter = getParameterWriterForEventType(entry.type); @@ -442,131 +435,6 @@ function reformatHeaders(entry) { } /** - * Removes a cookie or unencrypted login information from a single HTTP header - * line, if present, and returns the modified line. Otherwise, just returns - * the original line. - * - * Note: this logic should be kept in sync with - * net::ElideHeaderValueForNetLog in net/http/http_log_util.cc. - */ -function stripCookieOrLoginInfo(line) { - var patterns = [ - // Cookie patterns - /^set-cookie: /i, /^set-cookie2: /i, /^cookie: /i, - - // Unencrypted authentication patterns - /^authorization: \S*\s*/i, /^proxy-authorization: \S*\s*/i - ]; - - // Prefix will hold the first part of the string that contains no private - // information. If null, no part of the string contains private - // information. - var prefix = null; - for (var i = 0; i < patterns.length; i++) { - var match = patterns[i].exec(line); - if (match != null) { - prefix = match[0]; - break; - } - } - - // Look for authentication information from data received from the server in - // multi-round Negotiate authentication. - if (prefix === null) { - var challengePatterns = - [/^www-authenticate: (\S*)\s*/i, /^proxy-authenticate: (\S*)\s*/i]; - for (var i = 0; i < challengePatterns.length; i++) { - var match = challengePatterns[i].exec(line); - if (!match) - continue; - - // If there's no data after the scheme name, do nothing. - if (match[0].length == line.length) - break; - - // Ignore lines with commas, as they may contain lists of schemes, and - // the information we want to hide is Base64 encoded, so has no commas. - if (line.indexOf(',') >= 0) - break; - - // Ignore Basic and Digest authentication challenges, as they contain - // public information. - if (/^basic$/i.test(match[1]) || /^digest$/i.test(match[1])) - break; - - prefix = match[0]; - break; - } - } - - if (prefix) { - var suffix = line.slice(prefix.length); - // If private information has already been removed, keep the line as-is. - // This is often the case when viewing a loaded log. - if (suffix.search(/^\[[0-9]+ bytes were stripped\]$/) == -1) { - return prefix + '[' + suffix.length + ' bytes were stripped]'; - } - } - - return line; -} - -/** - * Remove debug data from HTTP/2 GOAWAY frame due to privacy considerations, - * see - * https://httpwg.github.io/specs/rfc7540.html#GOAWAY. - * - * Note: this logic should be kept in sync with - * net::ElideGoAwayDebugDataForNetLog in net/http/http_log_util.cc. - */ -function stripGoAwayDebugData(value) { - return '[' + value.length + ' bytes were stripped]'; -} - -/** - * If |entry| has headers, returns a copy of |entry| with all cookie and - * unencrypted login text removed. Otherwise, returns original |entry| - * object. - * This is needed so that JSON log dumps can be made without affecting the - * source data. Converts headers stored in objects to arrays. - */ -stripPrivacyInfo = function(entry) { - if (!entry.params) { - return entry; - } - - if (entry.type == EventType.HTTP2_SESSION_GOAWAY && - entry.params.debug_data != undefined) { - // Duplicate the top level object, and |entry.params|. All other fields - // are - // just pointers to the original values, as they won't be modified, other - // than |entry.params.debug_data|. - entry = shallowCloneObject(entry); - entry.params = shallowCloneObject(entry.params); - entry.params.debug_data = stripGoAwayDebugData(entry.params.debug_data); - return entry; - } - - if (entry.params.headers === undefined || - !(entry.params.headers instanceof Object)) { - return entry; - } - - // Make sure entry's headers are in an array. - entry = reformatHeaders(entry); - - // Duplicate the top level object, and |entry.params|. All other fields are - // just pointers to the original values, as they won't be modified, other - // than - // |entry.params.headers|. - entry = shallowCloneObject(entry); - entry.params = shallowCloneObject(entry.params); - - entry.params.headers = entry.params.headers.map(stripCookieOrLoginInfo); - return entry; -}; - -/** * Outputs the request header parameters of |entry| to |out|. */ function writeParamsForRequestHeaders(entry, out, consumedParams) { diff --git a/chromium/chrome/browser/resources/net_internals/main.js b/chromium/chrome/browser/resources/net_internals/main.js index 6b286ea8b9a..875a93381e3 100644 --- a/chromium/chrome/browser/resources/net_internals/main.js +++ b/chromium/chrome/browser/resources/net_internals/main.js @@ -125,10 +125,8 @@ var MainView = (function() { this.stopCapturing(); if (opt_fileName != undefined) { // If there's a file name, a log file was loaded, so swap out the status - // bar to indicate we're no longer capturing events. Also disable - // hiding cookies, so if the log dump has them, they'll be displayed. + // bar to indicate we're no longer capturing events. this.topBarView_.switchToSubView('loaded').setFileName(opt_fileName); - SourceTracker.getInstance().setPrivacyStripping(false); } else { // Otherwise, the "Stop Capturing" button was presumably pressed. // Don't disable hiding cookies, so created log dumps won't have them, @@ -145,8 +143,11 @@ var MainView = (function() { stopCapturing: function() { g_browser.disable(); - document.styleSheets[0].insertRule( - '.hide-when-not-capturing { display: none; }', 0); + var sheet = document.createElement('style'); + sheet.type = 'text/css'; + sheet.appendChild(document.createTextNode( + '.hide-when-not-capturing { display: none; }')); + document.head.appendChild(sheet); }, initTabs_: function() { diff --git a/chromium/chrome/browser/resources/net_internals/source_entry.js b/chromium/chrome/browser/resources/net_internals/source_entry.js index bb1db6fcd08..100d72ee66e 100644 --- a/chromium/chrome/browser/resources/net_internals/source_entry.js +++ b/chromium/chrome/browser/resources/net_internals/source_entry.js @@ -335,7 +335,7 @@ var SourceEntry = (function() { */ createTablePrinter: function() { return createLogEntryTablePrinter( - this.entries_, SourceTracker.getInstance().getPrivacyStripping(), + this.entries_, SourceTracker.getInstance().getUseRelativeTimes() ? timeutil.getBaseTime() : 0, diff --git a/chromium/chrome/browser/resources/net_internals/source_tracker.js b/chromium/chrome/browser/resources/net_internals/source_tracker.js index 9e870db49e5..9dce848de5e 100644 --- a/chromium/chrome/browser/resources/net_internals/source_tracker.js +++ b/chromium/chrome/browser/resources/net_internals/source_tracker.js @@ -18,11 +18,6 @@ var SourceTracker = (function() { // Observers that only want to receive lists of updated SourceEntries. this.sourceEntryObservers_ = []; - // True when cookies and authentication information should be removed from - // displayed events. When true, such information should be hidden from - // all pages. - this.privacyStripping_ = true; - // True when times should be displayed as milliseconds since the first // event, as opposed to milliseconds since January 1, 1970. this.useRelativeTimes_ = true; @@ -135,26 +130,6 @@ var SourceTracker = (function() { }, /** - * Sets the value of |privacyStripping_| and informs log observers - * of the change. - */ - setPrivacyStripping: function(privacyStripping) { - this.privacyStripping_ = privacyStripping; - for (var i = 0; i < this.sourceEntryObservers_.length; ++i) { - if (this.sourceEntryObservers_[i].onPrivacyStrippingChanged) - this.sourceEntryObservers_[i].onPrivacyStrippingChanged(); - } - }, - - /** - * Returns whether or not cookies and authentication information should be - * displayed for events that contain them. - */ - getPrivacyStripping: function() { - return this.privacyStripping_; - }, - - /** * Sets the value of |useRelativeTimes_| and informs log observers * of the change. */ @@ -176,12 +151,10 @@ var SourceTracker = (function() { /** * Adds a listener of SourceEntries. |observer| will be called back when - * SourceEntries are added or modified, source entries are deleted, or - * privacy stripping changes: + * SourceEntries are added or modified or source entries are deleted. * * observer.onSourceEntriesUpdated(sourceEntries) * observer.onAllSourceEntriesDeleted() - * observer.onPrivacyStrippingChanged() */ addSourceEntryObserver: function(observer) { this.sourceEntryObservers_.push(observer); diff --git a/chromium/chrome/browser/resources/ntp4/app_launcher_promo.png b/chromium/chrome/browser/resources/ntp4/app_launcher_promo.png Binary files differdeleted file mode 100644 index 7cbae25e752..00000000000 --- a/chromium/chrome/browser/resources/ntp4/app_launcher_promo.png +++ /dev/null diff --git a/chromium/chrome/browser/resources/ntp4/apps_page.css b/chromium/chrome/browser/resources/ntp4/apps_page.css index c64ad4a8740..48473fc2eea 100644 --- a/chromium/chrome/browser/resources/ntp4/apps_page.css +++ b/chromium/chrome/browser/resources/ntp4/apps_page.css @@ -99,64 +99,3 @@ .app .invisible { visibility: hidden; } - -#app-launcher-promo { - background-color: white; - border: 1px solid lightgray; - border-bottom-width: 3px; - border-radius: 2px; - border-top-width: 2px; - bottom: 90px; - height: 120px; - left: 50%; - margin-left: -300px; - position: fixed; - width: 600px; -} - -#app-launcher-promo > .close-button { - position: absolute; - right: 10px; - top: 10px; - width: 14px; -} - -.apps-promo-text { - color: #222; - font-size: 16px; - left: 30px; - line-height: 24px; - position: absolute; - top: 30px; -} - -.apps-promo-learn-more { - background-color: rgb(77, 144, 254); - border: 1px solid rgb(47, 91, 183); - border-radius: 2px; - color: white; - cursor: default; - font-size: 11px; - font-weight: bold; - height: 27px; - left: 30px; - line-height: 27px; - padding: 0 8px; - position: absolute; - text-align: center; - text-decoration: none; - top: 70px; - width: 90px; -} - -.apps-promo-learn-more:hover { - background-image: -webkit-linear-gradient( - top, rgb(77, 144, 254), rgb(53, 122, 232)); - border: 1px solid rgb(47, 91, 183); -} - -#app-launcher-promo > img { - bottom: 0; - position: absolute; - right: 30px; -} diff --git a/chromium/chrome/browser/resources/ntp4/incognito_tab.css b/chromium/chrome/browser/resources/ntp4/incognito_tab.css index 67bd8c9d471..3b52699d588 100644 --- a/chromium/chrome/browser/resources/ntp4/incognito_tab.css +++ b/chromium/chrome/browser/resources/ntp4/incognito_tab.css @@ -1,32 +1,300 @@ -/* Copyright 2013 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. */ body { - margin-top: 0; + -webkit-font-smoothing: antialiased; + font-size: 100%; + margin: 0; } +/** Typography -------------------------------------------------------------- */ + .content { - background-color: #323232; + /* This is identical to the default background color. It's necessary to set it + for the case when a theme with a background image is installed. */ + background-color: #303030; + color: rgba(255, 255, 255, 0.7); + font-size: calc(100% - 2px); + line-height: calc(100% + 6px); + min-width: 240px; +} + +h1 { + color: rgba(255, 255, 255, 0.8); + font-size: calc(100% + 8px); + font-weight: 400; + line-height: calc(100% + 8px); +} + +em { color: white; + font-style: normal; +} + +.learn-more-button { + color: rgb(123, 170, 247); + text-decoration: none; } +/* Small font on small screens. */ +@media (max-width: 240px), + (max-height: 320px) { + .content { + font-size: calc(100% - 4px); + line-height: calc(100% + 6px); + } + + h1 { + font-size: calc(100% + 4px); + line-height: calc(100% + 4px); + } +} + +/** Icon -------------------------------------------------------------------- */ + .icon { content: url(../../../../ui/webui/resources/images/incognito_splash.svg); - display: inline-block; height: 120px; width: 120px; } +/* Medium-sized icon on medium-sized screens. */ +@media (max-height: 480px), + (max-width: 720px) { + .icon { + height: 72px; + width: 72px; + } +} + +/* Very small icon on very small screens. */ +@media (max-width: 720px) { + @media (max-width: 240px), + (max-height: 480px) { + .icon { + height: 48px; + width: 48px; + } + } +} + +/** The "Learn more" link --------------------------------------------------- */ + +/* By default, we only show the inline "Learn more" link. */ +.content > .learn-more-button { + display: none; +} + +/* On narrow screens, we show the standalone "Learn more" link. */ +@media (max-width: 720px) { + #subtitle > .learn-more-button { + display: none; + } + + .content > .learn-more-button { + display: block; + } +} + +/** Layout ------------------------------------------------------------------ */ + +/* Align the content, icon, and title to to the center. */ +.content { + margin-left: auto; + margin-right: auto; + max-width: 600px; +} + +.icon { + margin-left: auto; + margin-right: auto; +} + h1 { - margin-top: .88em; - opacity: .8; + text-align: center; } -p { - opacity: .8; +/* Align the two columns of bulletpoints next to each other. */ +.bulletpoints { + float: left; } -.learn-more-button { - color: rgb(123, 170, 247); +html[dir=rtl] .bulletpoints { + float: right; +} + +.bulletpoints + .bulletpoints { + clear: right; +} + +html[dir=rtl] .bulletpoints + .bulletpoints { + clear: left; +} + +.clearer { + clear: both; +} + +/* On narrow screens, align everything to the left. */ +@media (max-width: 720px) { + .content { + max-width: 600px !important; /* must override the rule set by JS which + * is only valid for width > 720px cases. */ + text-align: start; + } + + .icon { + -webkit-margin-start: 0; + } + + h1 { + text-align: start; + } + + .bulletpoints + .bulletpoints, + html[dir=rtl] .bulletpoints + .bulletpoints { + clear: both; + } +} + +/** Paddings and margins ---------------------------------------------------- */ + +.bulletpoints ul { + -webkit-padding-start: 16px; + margin: 4px 0 0; +} + +/* Margins of floating elements don't collapse. The margin for bulletpoints + * will usually be provided by a neighboring element. */ +.bulletpoints { + margin: 0; +} + +.bulletpoints + .bulletpoints { + -webkit-margin-start: 40px; +} + +.bulletpoints + .bulletpoints.too-wide { + -webkit-margin-start: 0; + margin-top: 1.5rem; +} + +/* Wide screens. */ +@media (min-width: 720px) { + .icon, + h1, + #subtitle, + .learn-more-button { + margin-bottom: 1.5rem; + margin-top: 1.5rem; + } + + .content { + margin-top: 40px; + min-width: 240px; + padding: 8px 48px 24px; + } + + /* Snap the content box to the whole height on short screens. */ + @media (max-height: 480px) { + html, + body, + .content { + height: 100%; + } + + .content { + margin-bottom: 0; + margin-top: 0; + padding-bottom: 0; + padding-top: 0; + } + + .icon { + margin-top: 0; + padding-top: 32px; /* Define the top offset through the icon's padding, + * otherwise the screen height would be 100% + 32px */ + } + } + + /* Smaller vertical margins on very short screens. */ + @media (max-height: 320px) { + h1, + #subtitle, + .learn-more-button { + margin-bottom: 16px; + margin-top: 16px; + } + + .icon { + margin-bottom: 16px; + } + } +} + +/* Narrow screens */ +@media (max-width: 720px) { + .content { + padding: 72px 32px; + min-width: 176px; + } + + .icon, + h1, + #subtitle, + .learn-more-button { + margin-bottom: 1.5rem; + margin-top: 1.5rem; + } + + /* The two columns of bulletpoints are moved under each other. */ + .bulletpoints + .bulletpoints { + -webkit-margin-start: 0; + margin-top: 1.5rem; + } + + /* Smaller offsets on smaller screens. */ + @media (max-height: 600px) { + .content { + padding-top: 48px; + } + + .icon, + h1, + #subtitle, + .learn-more-button { + margin-bottom: 1rem; + margin-top: 1rem; + } + + .bulletpoints + .bulletpoints { + margin-top: 1rem; + } + } + + /* Small top offset on very small screens. */ + @media (max-height: 480px) { + .content { + padding-top: 32px; + } + } + + /* Undo the first and last elements margins. */ + .icon { + margin-top: 0; + } + + .learn-more-button { + margin-bottom: 0; + } +} + +/* Very narrow screens. */ +@media (max-width: 240px) { + .content { + min-width: 192px; + padding-left: 24px; + padding-right: 24px; + } } diff --git a/chromium/chrome/browser/resources/ntp4/incognito_tab.html b/chromium/chrome/browser/resources/ntp4/incognito_tab.html index 5d4a5198c14..52dce6e017b 100644 --- a/chromium/chrome/browser/resources/ntp4/incognito_tab.html +++ b/chromium/chrome/browser/resources/ntp4/incognito_tab.html @@ -1,14 +1,14 @@ <!doctype html> <html dir="$i18n{textdirection}" - hascustombackground="$i18n{hasCustomBackground}" - bookmarkbarattached="$i18n{bookmarkbarattached}" - lang="$i18n{language}"> + hascustombackground="$i18n{hasCustomBackground}" + bookmarkbarattached="$i18n{bookmarkbarattached}" + lang="$i18n{language}" + class="md"> <head> <meta charset="utf-8"> <title>$i18n{title}</title> <meta name="viewport" content="width=device-width"> <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> -<link rel="stylesheet" href="incognito_and_guest_tab.css"> <link rel="stylesheet" href="incognito_tab.css"> <script> // Until themes can clear the cache, force-reload the theme stylesheet. @@ -19,28 +19,22 @@ document.write('<link id="incognitothemecss" rel="stylesheet" ' + </head> <body> <div class="content"> - <div class="icon"></div> + <div class="icon" role="presentation" alt=""></div> <h1>$i18n{incognitoTabHeading}</h1> - <p>$i18n{incognitoTabDescription}</p> - <p>$i18n{incognitoTabWarning}</p> + <p id="subtitle"> + <span>$i18n{incognitoTabDescription}</span> + <a class="learn-more-button" + href="$i18n{learnMoreLink}">$i18n{learnMore}</a> + </p> + <div> + <div class="bulletpoints">$i18nRaw{incognitoTabFeatures}</div> + <div class="bulletpoints">$i18nRaw{incognitoTabWarning}</div> + <div class="clearer"></div> + </div> <a class="learn-more-button" href="$i18n{learnMoreLink}">$i18n{learnMore}</a> </div> - -<script> -var ntp = { - /** @param {string} attached */ - setBookmarkBarAttached: function(attached) { - document.documentElement.setAttribute('bookmarkbarattached', attached); - }, - - /** @param {!{hasCustomBackground: boolean}} themeData */ - themeChanged: function(themeData) { - document.documentElement.setAttribute('hascustombackground', - themeData.hasCustomBackground); - document.getElementById('incognitothemecss').href = - 'chrome://theme/css/incognito_new_tab_theme.css?' + Date.now(); - }, -}; -</script> +<script src="chrome://resources/js/cr.js"></script> +<script src="chrome://resources/js/util.js"></script> +<script src="incognito_tab.js"></script> </body> </html> diff --git a/chromium/chrome/browser/resources/ntp4/md_incognito_tab.js b/chromium/chrome/browser/resources/ntp4/incognito_tab.js index 4b246225fb8..4b246225fb8 100644 --- a/chromium/chrome/browser/resources/ntp4/md_incognito_tab.js +++ b/chromium/chrome/browser/resources/ntp4/incognito_tab.js diff --git a/chromium/chrome/browser/resources/ntp4/md_incognito_tab.css b/chromium/chrome/browser/resources/ntp4/md_incognito_tab.css deleted file mode 100644 index 3b52699d588..00000000000 --- a/chromium/chrome/browser/resources/ntp4/md_incognito_tab.css +++ /dev/null @@ -1,300 +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. */ - -body { - -webkit-font-smoothing: antialiased; - font-size: 100%; - margin: 0; -} - -/** Typography -------------------------------------------------------------- */ - -.content { - /* This is identical to the default background color. It's necessary to set it - for the case when a theme with a background image is installed. */ - background-color: #303030; - color: rgba(255, 255, 255, 0.7); - font-size: calc(100% - 2px); - line-height: calc(100% + 6px); - min-width: 240px; -} - -h1 { - color: rgba(255, 255, 255, 0.8); - font-size: calc(100% + 8px); - font-weight: 400; - line-height: calc(100% + 8px); -} - -em { - color: white; - font-style: normal; -} - -.learn-more-button { - color: rgb(123, 170, 247); - text-decoration: none; -} - -/* Small font on small screens. */ -@media (max-width: 240px), - (max-height: 320px) { - .content { - font-size: calc(100% - 4px); - line-height: calc(100% + 6px); - } - - h1 { - font-size: calc(100% + 4px); - line-height: calc(100% + 4px); - } -} - -/** Icon -------------------------------------------------------------------- */ - -.icon { - content: url(../../../../ui/webui/resources/images/incognito_splash.svg); - height: 120px; - width: 120px; -} - -/* Medium-sized icon on medium-sized screens. */ -@media (max-height: 480px), - (max-width: 720px) { - .icon { - height: 72px; - width: 72px; - } -} - -/* Very small icon on very small screens. */ -@media (max-width: 720px) { - @media (max-width: 240px), - (max-height: 480px) { - .icon { - height: 48px; - width: 48px; - } - } -} - -/** The "Learn more" link --------------------------------------------------- */ - -/* By default, we only show the inline "Learn more" link. */ -.content > .learn-more-button { - display: none; -} - -/* On narrow screens, we show the standalone "Learn more" link. */ -@media (max-width: 720px) { - #subtitle > .learn-more-button { - display: none; - } - - .content > .learn-more-button { - display: block; - } -} - -/** Layout ------------------------------------------------------------------ */ - -/* Align the content, icon, and title to to the center. */ -.content { - margin-left: auto; - margin-right: auto; - max-width: 600px; -} - -.icon { - margin-left: auto; - margin-right: auto; -} - -h1 { - text-align: center; -} - -/* Align the two columns of bulletpoints next to each other. */ -.bulletpoints { - float: left; -} - -html[dir=rtl] .bulletpoints { - float: right; -} - -.bulletpoints + .bulletpoints { - clear: right; -} - -html[dir=rtl] .bulletpoints + .bulletpoints { - clear: left; -} - -.clearer { - clear: both; -} - -/* On narrow screens, align everything to the left. */ -@media (max-width: 720px) { - .content { - max-width: 600px !important; /* must override the rule set by JS which - * is only valid for width > 720px cases. */ - text-align: start; - } - - .icon { - -webkit-margin-start: 0; - } - - h1 { - text-align: start; - } - - .bulletpoints + .bulletpoints, - html[dir=rtl] .bulletpoints + .bulletpoints { - clear: both; - } -} - -/** Paddings and margins ---------------------------------------------------- */ - -.bulletpoints ul { - -webkit-padding-start: 16px; - margin: 4px 0 0; -} - -/* Margins of floating elements don't collapse. The margin for bulletpoints - * will usually be provided by a neighboring element. */ -.bulletpoints { - margin: 0; -} - -.bulletpoints + .bulletpoints { - -webkit-margin-start: 40px; -} - -.bulletpoints + .bulletpoints.too-wide { - -webkit-margin-start: 0; - margin-top: 1.5rem; -} - -/* Wide screens. */ -@media (min-width: 720px) { - .icon, - h1, - #subtitle, - .learn-more-button { - margin-bottom: 1.5rem; - margin-top: 1.5rem; - } - - .content { - margin-top: 40px; - min-width: 240px; - padding: 8px 48px 24px; - } - - /* Snap the content box to the whole height on short screens. */ - @media (max-height: 480px) { - html, - body, - .content { - height: 100%; - } - - .content { - margin-bottom: 0; - margin-top: 0; - padding-bottom: 0; - padding-top: 0; - } - - .icon { - margin-top: 0; - padding-top: 32px; /* Define the top offset through the icon's padding, - * otherwise the screen height would be 100% + 32px */ - } - } - - /* Smaller vertical margins on very short screens. */ - @media (max-height: 320px) { - h1, - #subtitle, - .learn-more-button { - margin-bottom: 16px; - margin-top: 16px; - } - - .icon { - margin-bottom: 16px; - } - } -} - -/* Narrow screens */ -@media (max-width: 720px) { - .content { - padding: 72px 32px; - min-width: 176px; - } - - .icon, - h1, - #subtitle, - .learn-more-button { - margin-bottom: 1.5rem; - margin-top: 1.5rem; - } - - /* The two columns of bulletpoints are moved under each other. */ - .bulletpoints + .bulletpoints { - -webkit-margin-start: 0; - margin-top: 1.5rem; - } - - /* Smaller offsets on smaller screens. */ - @media (max-height: 600px) { - .content { - padding-top: 48px; - } - - .icon, - h1, - #subtitle, - .learn-more-button { - margin-bottom: 1rem; - margin-top: 1rem; - } - - .bulletpoints + .bulletpoints { - margin-top: 1rem; - } - } - - /* Small top offset on very small screens. */ - @media (max-height: 480px) { - .content { - padding-top: 32px; - } - } - - /* Undo the first and last elements margins. */ - .icon { - margin-top: 0; - } - - .learn-more-button { - margin-bottom: 0; - } -} - -/* Very narrow screens. */ -@media (max-width: 240px) { - .content { - min-width: 192px; - padding-left: 24px; - padding-right: 24px; - } -} diff --git a/chromium/chrome/browser/resources/ntp4/md_incognito_tab.html b/chromium/chrome/browser/resources/ntp4/md_incognito_tab.html deleted file mode 100644 index 5cad43b07bd..00000000000 --- a/chromium/chrome/browser/resources/ntp4/md_incognito_tab.html +++ /dev/null @@ -1,40 +0,0 @@ -<!doctype html> -<html dir="$i18n{textdirection}" - hascustombackground="$i18n{hasCustomBackground}" - bookmarkbarattached="$i18n{bookmarkbarattached}" - lang="$i18n{language}" - class="md"> -<head> -<meta charset="utf-8"> -<title>$i18n{title}</title> -<meta name="viewport" content="width=device-width"> -<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> -<link rel="stylesheet" href="md_incognito_tab.css"> -<script> -// Until themes can clear the cache, force-reload the theme stylesheet. -document.write('<link id="incognitothemecss" rel="stylesheet" ' + - 'href="chrome://theme/css/incognito_new_tab_theme.css?' + - Date.now() + '">'); -</script> -</head> -<body> -<div class="content"> - <div class="icon" role="presentation" alt=""></div> - <h1>$i18n{incognitoTabHeading}</h1> - <p id="subtitle"> - <span>$i18n{incognitoTabDescription}</span> - <a class="learn-more-button" - href="$i18n{learnMoreLink}">$i18n{learnMore}</a> - </p> - <div> - <div class="bulletpoints">$i18nRaw{incognitoTabFeatures}</div> - <div class="bulletpoints">$i18nRaw{incognitoTabWarning}</div> - <div class="clearer"></div> - </div> - <a class="learn-more-button" href="$i18n{learnMoreLink}">$i18n{learnMore}</a> -</div> -<script src="chrome://resources/js/cr.js"></script> -<script src="chrome://resources/js/util.js"></script> -<script src="md_incognito_tab.js"></script> -</body> -</html> diff --git a/chromium/chrome/browser/resources/ntp4/new_tab.html b/chromium/chrome/browser/resources/ntp4/new_tab.html index bc44bba405b..d6f57b23e2a 100644 --- a/chromium/chrome/browser/resources/ntp4/new_tab.html +++ b/chromium/chrome/browser/resources/ntp4/new_tab.html @@ -78,18 +78,6 @@ document.write('<link id="themecss" rel="stylesheet" ' + </div> </div> - <div id="app-launcher-promo" hidden> - <div class="apps-promo-text">$i18n{appsPromoTitle}</div> - <a href="https://chrome.google.com/webstore/launcher" - id="apps-promo-learn-more" class="apps-promo-learn-more"> - $i18n{learn_more} - </a> - <img src="app_launcher_promo.png"> - <button class="close-button custom-appearance" - id="app-launcher-promo-close-button"> - </button> - </div> - <div id="footer"> <div id="footer-border"></div> <div id="footer-content"> diff --git a/chromium/chrome/browser/resources/ntp4/new_tab.js b/chromium/chrome/browser/resources/ntp4/new_tab.js index bcf8a7b028b..c6564c4f062 100644 --- a/chromium/chrome/browser/resources/ntp4/new_tab.js +++ b/chromium/chrome/browser/resources/ntp4/new_tab.js @@ -76,15 +76,6 @@ cr.define('ntp', function() { */ function onLoad() { 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(); newTabView = new NewTabView(); diff --git a/chromium/chrome/browser/resources/ntp4/page_list_view.js b/chromium/chrome/browser/resources/ntp4/page_list_view.js index 89bb392b521..3f2e99b8ca5 100644 --- a/chromium/chrome/browser/resources/ntp4/page_list_view.js +++ b/chromium/chrome/browser/resources/ntp4/page_list_view.js @@ -402,7 +402,6 @@ cr.define('ntp', function() { this.appsLoaded_ = true; cr.dispatchSimpleEvent(document, 'sectionready', true, true); } - this.updateAppLauncherPromoHiddenState_(); }, /** @@ -460,25 +459,6 @@ cr.define('ntp', function() { }, /** - * Callback invoked by chrome whenever the app launcher promo pref changes. - * @param {boolean} show Identifies if we should show or hide the promo. - */ - appLauncherPromoPrefChangeCallback: function(show) { - loadTimeData.overrideValues({showAppLauncherPromo: show}); - this.updateAppLauncherPromoHiddenState_(); - }, - - /** - * Updates the hidden state of the app launcher promo based on the page - * shown and load data content. - * @private - */ - updateAppLauncherPromoHiddenState_: function() { - $('app-launcher-promo').hidden = - !loadTimeData.getBoolean('showAppLauncherPromo'); - }, - - /** * Invoked whenever the pages in apps-page-list have changed so that * the Slider knows about the new elements. */ @@ -632,7 +612,6 @@ cr.define('ntp', function() { assert(shownPageIndex >= 0); this.shownPageIndex = shownPageIndex; chrome.send('pageSelected', [this.shownPageIndex]); - this.updateAppLauncherPromoHiddenState_(); }, /** diff --git a/chromium/chrome/browser/resources/offline_pages/offline_internals.css b/chromium/chrome/browser/resources/offline_pages/offline_internals.css index 534ae985fa9..d8ad7a582d6 100644 --- a/chromium/chrome/browser/resources/offline_pages/offline_internals.css +++ b/chromium/chrome/browser/resources/offline_pages/offline_internals.css @@ -47,6 +47,10 @@ li:nth-child(2n) { background-color: lavender; } +dialog { + border: none; +} + #current-status { font-size: 15px; } @@ -55,3 +59,10 @@ li:nth-child(2n) { font-family: monospace; white-space: pre-wrap; } + +#dump-box { + box-sizing: border-box; + display: block; + resize: none; + width: 100%; +} diff --git a/chromium/chrome/browser/resources/offline_pages/offline_internals.html b/chromium/chrome/browser/resources/offline_pages/offline_internals.html index 2590edbda99..013cfd09390 100644 --- a/chromium/chrome/browser/resources/offline_pages/offline_internals.html +++ b/chromium/chrome/browser/resources/offline_pages/offline_internals.html @@ -19,8 +19,15 @@ <div> <span id="current-status"></span> <button id="refresh">Refresh page</button> - <button id="download">Dump</button> + <button id="dump">Dump</button> </div> + <dialog id="dump-modal"> + <textarea id="dump-box" name="json-box" rows="10" cols="40" readonly> + </textarea> + <button id="copy-to-clipboard">Copy</button> + <button id="close-dump">Close</button> + <span id="dump-info"></span> + </dialog> <h2>Event Logs</h2> <div> diff --git a/chromium/chrome/browser/resources/offline_pages/offline_internals.js b/chromium/chrome/browser/resources/offline_pages/offline_internals.js index 2605b0b781a..d9034ccda18 100644 --- a/chromium/chrome/browser/resources/offline_pages/offline_internals.js +++ b/chromium/chrome/browser/resources/offline_pages/offline_internals.js @@ -35,18 +35,18 @@ cr.define('offlineInternals', function() { var template = $('stored-pages-table-row'); var td = template.content.querySelectorAll('td'); - for (var i = 0; i < pages.length; i++) { + for (let page of pages) { var checkbox = td[0].querySelector('input'); - checkbox.setAttribute('value', pages[i].id); + checkbox.setAttribute('value', page.id); var link = td[1].querySelector('a'); - link.setAttribute('href', pages[i].onlineUrl); - link.textContent = pages[i].onlineUrl; + link.setAttribute('href', page.onlineUrl); + link.textContent = page.onlineUrl; - td[2].textContent = pages[i].namespace; - td[3].textContent = Math.round(pages[i].size / 1024); - td[4].textContent = pages[i].isExpired; - td[5].textContent = pages[i].requestOrigin; + td[2].textContent = page.namespace; + td[3].textContent = Math.round(page.size / 1024); + td[4].textContent = page.isExpired; + td[5].textContent = page.requestOrigin; var row = document.importNode(template.content, true); storedPagesTable.appendChild(row); @@ -65,14 +65,14 @@ cr.define('offlineInternals', function() { var template = $('request-queue-table-row'); var td = template.content.querySelectorAll('td'); - for (var i = 0; i < requests.length; i++) { + for (let request of requests) { var checkbox = td[0].querySelector('input'); - checkbox.setAttribute('value', requests[i].id); + checkbox.setAttribute('value', request.id); - td[1].textContent = requests[i].onlineUrl; - td[2].textContent = new Date(requests[i].creationTime); - td[3].textContent = requests[i].status; - td[4].textContent = requests[i].requestOrigin; + td[1].textContent = request.onlineUrl; + td[2].textContent = new Date(request.creationTime); + td[3].textContent = request.status; + td[4].textContent = request.requestOrigin; var row = document.importNode(template.content, true); requestQueueTable.appendChild(row); @@ -171,15 +171,33 @@ cr.define('offlineInternals', function() { /** * Downloads all the stored page and request queue information into a file. + * Also translates all the fields representing datetime into human-readable + * date strings. * TODO(chili): Create a CSV writer that can abstract out the line joining. */ - function download() { + function dumpAsJson() { var json = JSON.stringify( - {offlinePages: offlinePages, savePageRequests: savePageRequests}, null, + {offlinePages: offlinePages, savePageRequests: savePageRequests}, + function(key, value) { + return key.endsWith('Time') ? new Date(value).toString() : value; + }, 2); - window.open( - 'data:application/json,' + encodeURIComponent(json), 'dump.json'); + $('dump-box').value = json; + $('dump-info').textContent = ''; + $('dump-modal').showModal(); + $('dump-box').select(); + } + + function closeDump() { + $('dump-modal').close(); + $('dump-box').value = ''; + } + + function copyDump() { + $('dump-box').select(); + document.execCommand('copy'); + $('dump-info').textContent = 'Copied to clipboard!'; } /** @@ -260,22 +278,19 @@ cr.define('offlineInternals', function() { } var incognito = loadTimeData.getBoolean('isIncognito'); - $('delete-all-pages').disabled = incognito; - $('delete-selected-pages').disabled = incognito; - $('delete-all-requests').disabled = incognito; - $('delete-selected-requests').disabled = incognito; - $('log-model-on').disabled = incognito; - $('log-model-off').disabled = incognito; - $('log-request-on').disabled = incognito; - $('log-request-off').disabled = incognito; - $('refresh').disabled = incognito; + ['delete-all-pages', 'delete-selected-pages', 'delete-all-requests', + 'delete-selected-requests', 'log-model-on', 'log-model-off', + 'log-request-on', 'log-request-off', 'refresh'] + .forEach(el => $(el).disabled = incognito); $('delete-all-pages').onclick = deleteAllPages; $('delete-selected-pages').onclick = deleteSelectedPages; $('delete-all-requests').onclick = deleteAllRequests; $('delete-selected-requests').onclick = deleteSelectedRequests; $('refresh').onclick = refreshAll; - $('download').onclick = download; + $('dump').onclick = dumpAsJson; + $('close-dump').onclick = closeDump; + $('copy-to-clipboard').onclick = copyDump; $('log-model-on').onclick = togglePageModelLog.bind(this, true); $('log-model-off').onclick = togglePageModelLog.bind(this, false); $('log-request-on').onclick = toggleRequestQueueLog.bind(this, true); 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 b30b277609c..04edadc8093 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 @@ -25,7 +25,7 @@ var OfflinePage; * creationTime: number, * id: string, * namespace: string, - * lastAttempt: number, + * lastAttemptTime: number, * requestOrigin: string * }} */ diff --git a/chromium/chrome/browser/resources/password_manager_internals/OWNERS b/chromium/chrome/browser/resources/password_manager_internals/OWNERS deleted file mode 100644 index e8e1954be35..00000000000 --- a/chromium/chrome/browser/resources/password_manager_internals/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -mkwst@chromium.org -vabr@chromium.org - -# COMPONENT: UI>Browser>Passwords diff --git a/chromium/chrome/browser/resources/password_manager_internals/password_manager_internals.css b/chromium/chrome/browser/resources/password_manager_internals/password_manager_internals.css deleted file mode 100644 index 09a6603607a..00000000000 --- a/chromium/chrome/browser/resources/password_manager_internals/password_manager_internals.css +++ /dev/null @@ -1,31 +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. - * - * chrome://password-manager-internals/ */ - -#logging-note { - font-style: italic; -} - -#logging-note-incognito { - font-style: italic; -} - -/* Initially, nothing is visible, to avoid flicker. */ -#log-entries, -#logging-note, -#logging-note-incognito { - display: none; -} - -/* Visibility settings for non-Incognito tabs. */ -[data-incognito=false] #log-entries, -[data-incognito=false] #logging-note { - display: block; -} - -/* Visibility settings for Incognito tabs. */ -[data-incognito=true] #logging-note-incognito { - display: block; -} diff --git a/chromium/chrome/browser/resources/password_manager_internals/password_manager_internals.html b/chromium/chrome/browser/resources/password_manager_internals/password_manager_internals.html deleted file mode 100644 index 0339d394d7a..00000000000 --- a/chromium/chrome/browser/resources/password_manager_internals/password_manager_internals.html +++ /dev/null @@ -1,25 +0,0 @@ -<!doctype html> -<html> -<head> -<!-- 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. --> -<script src="chrome://resources/js/util.js"></script> -<script src="chrome://password-manager-internals/password_manager_internals.js"></script> -<title>Password Manager Internals</title> -<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css"> -<link rel="stylesheet" href="password_manager_internals.css"> -</head> -<body> -<h1>Password Manager Internals</h1> -<div id="logging-note"> - Captured password manager logs are listed below. Logs are cleared and no - longer captured when all password-manager-internals pages are closed. -</div> -<div id="logging-note-incognito"> - Captured password manager logs are <em>not available</em> in Incognito. -</div> -<div id="log-entries"> -</div> -</body> -</html> diff --git a/chromium/chrome/browser/resources/password_manager_internals/password_manager_internals.js b/chromium/chrome/browser/resources/password_manager_internals/password_manager_internals.js deleted file mode 100644 index ca5637e01b5..00000000000 --- a/chromium/chrome/browser/resources/password_manager_internals/password_manager_internals.js +++ /dev/null @@ -1,17 +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. - -function addSavePasswordProgressLog(logText) { - var logDiv = $('log-entries'); - if (!logDiv) - return; - logDiv.appendChild(document.createElement('hr')); - var textDiv = document.createElement('div'); - textDiv.innerText = logText; - logDiv.appendChild(textDiv); -} - -function notifyAboutIncognito(isIncognito) { - document.body.dataset.incognito = isIncognito; -} diff --git a/chromium/chrome/browser/resources/password_manager_internals_resources.grd b/chromium/chrome/browser/resources/password_manager_internals_resources.grd deleted file mode 100644 index ed63cbbeb1a..00000000000 --- a/chromium/chrome/browser/resources/password_manager_internals_resources.grd +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> - <outputs> - <output filename="grit/password_manager_internals_resources.h" type="rc_header"> - <emit emit_type='prepend'></emit> - </output> - <output filename="password_manager_internals_resources.pak" type="data_package" /> - </outputs> - <release seq="1"> - <includes> - <include name="IDR_PASSWORD_MANAGER_INTERNALS_PASSWORD_MANAGER_INTERNALS_HTML" file="password_manager_internals/password_manager_internals.html" flattenhtml="true" allowexternalscript="false" compress="gzip" type="BINDATA" /> - <include name="IDR_PASSWORD_MANAGER_INTERNALS_PASSWORD_MANAGER_INTERNALS_JS" file="password_manager_internals/password_manager_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" /> - <include name="IDR_PASSWORD_MANAGER_INTERNALS_PASSWORD_MANAGER_INTERNALS_CSS" file="password_manager_internals/password_manager_internals.css" flattenhtml="true" compress="gzip" type="BINDATA" /> - </includes> - </release> -</grit> diff --git a/chromium/chrome/browser/resources/pdf/compiled_resources2.gyp b/chromium/chrome/browser/resources/pdf/compiled_resources2.gyp index 05a86018b37..655323d32f2 100644 --- a/chromium/chrome/browser/resources/pdf/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/pdf/compiled_resources2.gyp @@ -14,12 +14,19 @@ 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { + 'target_name': 'pdf_fitting_type', + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'gesture_detector', 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, { 'target_name': 'open_pdf_params_parser', 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + 'dependencies': [ + 'pdf_fitting_type', + ], }, { 'target_name': 'pdf_scripting_api', diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html index e0edf8a39e2..8ada7527043 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html @@ -7,7 +7,7 @@ <style> :host { color: #fff; - font-size: 123%; + font-size: 1.23rem; } :host ::selection { @@ -47,9 +47,10 @@ text-align: start; } + #input, #slash, #pagelength { - font-size: 81%; + font-size: 0.81rem; } </style> <paper-input-container id="pageselector" no-label-float> 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 9ca3d05e7c8..1a99c68e78a 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 @@ -29,7 +29,7 @@ #title { @apply(--layout-flex-5); - font-size: 107%; + font-size: 0.87rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html index 83947a3e12f..2e3e6182753 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html @@ -58,7 +58,7 @@ h1 { border-bottom: 1px solid rgb(219, 219, 219); - font-size: 107.6%; + font-size: 0.87rem; font-weight: 500; margin: 0; padding: 14px 28px; diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js index f76c8cb596b..565cc488334 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js @@ -4,8 +4,8 @@ (function() { -var FIT_TO_PAGE = 0; -var FIT_TO_WIDTH = 1; +var FIT_TO_PAGE_BUTTON_STATE = 0; +var FIT_TO_WIDTH_BUTTON_STATE = 1; Polymer({ is: 'viewer-zoom-toolbar', @@ -35,10 +35,10 @@ Polymer({ * Handle clicks of the fit-button. */ fitToggle: function() { - if (this.$['fit-button'].activeIndex == FIT_TO_WIDTH) - this.fire('fit-to-width'); - else - this.fire('fit-to-page'); + this.fireFitToChangedEvent_( + this.$['fit-button'].activeIndex == FIT_TO_WIDTH_BUTTON_STATE ? + FittingType.FIT_TO_WIDTH : + FittingType.FIT_TO_PAGE); }, /** @@ -49,21 +49,33 @@ Polymer({ // Toggle the button state since there was no mouse click. var button = this.$['fit-button']; - if (button.activeIndex == FIT_TO_WIDTH) - button.activeIndex = FIT_TO_PAGE; - else - button.activeIndex = FIT_TO_WIDTH; + button.activeIndex = + (button.activeIndex == FIT_TO_WIDTH_BUTTON_STATE ? + FIT_TO_PAGE_BUTTON_STATE : + FIT_TO_WIDTH_BUTTON_STATE); }, /** - * Handle forcing zoom to fit-to-page via scripting. + * Handle forcing zoom via scripting to a fitting type. + * @param {FittingType} fittingType Page fitting type to force. */ - forceFitToPage: function() { - this.fire('fit-to-page'); + forceFit: function(fittingType) { + this.fireFitToChangedEvent_(fittingType); + + // Set the button state since there was no mouse click. + var nextButtonState = + (fittingType == FittingType.FIT_TO_WIDTH ? FIT_TO_PAGE_BUTTON_STATE : + FIT_TO_WIDTH_BUTTON_STATE); + this.$['fit-button'].activeIndex = nextButtonState; + }, - // Set the button state since there was no mouse click. Since the zoom is - // set to fit-to-page, the button should do fit-to-width on the next click. - this.$['fit-button'].activeIndex = FIT_TO_WIDTH; + /** + * @private + * Fire a 'fit-to-changed' {CustomEvent} with the given FittingType as detail. + * @param {FittingType} fittingType to include as payload. + */ + fireFitToChangedEvent_: function(fittingType) { + this.fire('fit-to-changed', fittingType); }, /** diff --git a/chromium/chrome/browser/resources/pdf/gesture_detector.js b/chromium/chrome/browser/resources/pdf/gesture_detector.js index 811bfe28b5b..2af88ee0349 100644 --- a/chromium/chrome/browser/resources/pdf/gesture_detector.js +++ b/chromium/chrome/browser/resources/pdf/gesture_detector.js @@ -22,18 +22,17 @@ class GestureDetector { /** @type {function(!Event)} */ (this.onTouchStart_.bind(this)), {passive: true}); + let boundOnTouch = + /** @type {function(!Event)} */ (this.onTouch_.bind(this)); + this.element_.addEventListener('touchmove', boundOnTouch, {passive: false}); + this.element_.addEventListener('touchend', boundOnTouch, {passive: true}); this.element_.addEventListener( - 'touchmove', - /** @type {function(!Event)} */ (this.onTouch_.bind(this)), - {passive: false}); - this.element_.addEventListener( - 'touchend', - /** @type {function(!Event)} */ (this.onTouch_.bind(this)), - {passive: true}); + 'touchcancel', boundOnTouch, {passive: true}); + this.element_.addEventListener( - 'touchcancel', - /** @type {function(!Event)} */ (this.onTouch_.bind(this)), - {passive: true}); + 'wheel', + /** @type {function(!Event)} */ (this.onWheel_.bind(this)), + {passive: false}); this.pinchStartEvent_ = null; this.lastTouchTouchesCount_ = 0; @@ -41,6 +40,19 @@ class GestureDetector { /** @private {?TouchEvent} */ this.lastEvent_ = null; + /** + * The scale relative to the start of the pinch when handling ctrl-wheels. + * null when there is no ongoing pinch. + * @private {?number} + */ + this.accumulatedWheelScale_ = null; + /** + * A timeout ID from setTimeout used for sending the pinchend event when + * handling ctrl-wheels. + * @private {?number} + */ + this.wheelEndTimeout_ = null; + /** @private {!Map<string, !Array<!Function>>} */ this.listeners_ = new Map([['pinchstart', []], ['pinchupdate', []], ['pinchend', []]]); @@ -140,6 +152,63 @@ class GestureDetector { } /** + * The callback for wheel events on the element. + * @private + * @param {!WheelEvent} event Wheel event on the element. + */ + onWheel_(event) { + // We handle ctrl-wheels to invoke our own pinch zoom. On Mac, synthetic + // ctrl-wheels are created from trackpad pinches. We handle these ourselves + // to prevent the browser's native pinch zoom. We also use our pinch + // zooming mechanism for handling non-synthetic ctrl-wheels. This allows us + // to anchor the zoom around the mouse position instead of the scroll + // position. + if (!event.ctrlKey) + return; + + event.preventDefault(); + + let wheelScale = Math.exp(-event.deltaY / 100); + // Clamp scale changes from the wheel event as they can be + // quite dramatic for non-synthetic ctrl-wheels. + let scale = Math.min(1.25, Math.max(0.75, wheelScale)); + let position = {x: event.clientX, y: event.clientY}; + + if (this.accumulatedWheelScale_ == null) { + this.accumulatedWheelScale_ = 1.0; + this.notify_({type: 'pinchstart', center: position}); + } + + this.accumulatedWheelScale_ *= scale; + this.notify_({ + type: 'pinchupdate', + scaleRatio: scale, + direction: scale > 1.0 ? 'in' : 'out', + startScaleRatio: this.accumulatedWheelScale_, + center: position + }); + + // We don't get any phase information for the ctrl-wheels, so we don't know + // when the gesture ends. We'll just use a timeout to send the pinch end + // event a short time after the last ctrl-wheel we see. + if (this.wheelEndTimeout_ != null) { + window.clearTimeout(this.wheelEndTimeout_); + this.wheelEndTimeout_ = null; + } + let gestureEndDelayMs = 100; + let endEvent = { + type: 'pinchend', + startScaleRatio: this.accumulatedWheelScale_, + center: position + }; + this.wheelEndTimeout_ = window.setTimeout(function(endEvent) { + this.notify_(endEvent); + this.wheelEndTimeout_ = null; + this.accumulatedWheelScale_ = null; + }.bind(this), gestureEndDelayMs, endEvent); + } + + /** * Computes the change in scale between this touch event * and a previous one. * @private diff --git a/chromium/chrome/browser/resources/pdf/index.html b/chromium/chrome/browser/resources/pdf/index.html index 8382c4b8525..96a13be132e 100644 --- a/chromium/chrome/browser/resources/pdf/index.html +++ b/chromium/chrome/browser/resources/pdf/index.html @@ -28,6 +28,7 @@ <viewer-error-screen id="error-screen"></viewer-error-screen> </body> +<script src="pdf_fitting_type.js"></script> <script src="toolbar_manager.js"></script> <script src="viewport.js"></script> <script src="open_pdf_params_parser.js"></script> diff --git a/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js b/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js index 2eefdec662e..e573656c3fd 100644 --- a/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js +++ b/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +var OpenPDFParamsParser; + +(function() { + 'use strict'; /** @@ -11,34 +15,32 @@ * the page number for a named destination. * @constructor */ -function OpenPDFParamsParser(getNamedDestinationsFunction) { +OpenPDFParamsParser = function(getNamedDestinationsFunction) { this.outstandingRequests_ = []; this.getNamedDestinationsFunction_ = getNamedDestinationsFunction; -} +}; OpenPDFParamsParser.prototype = { /** * @private - * Parse zoom parameter of open PDF parameters. If this - * parameter is passed while opening PDF then PDF should be opened - * at the specified zoom level. + * Parse zoom parameter of open PDF parameters. The PDF should be opened at + * the specified zoom level. * @param {string} paramValue zoom value. - * @param {Object} viewportPosition to store zoom and position value. + * @return {Object} Map with zoom parameters (zoom and position). */ - parseZoomParam_: function(paramValue, viewportPosition) { + parseZoomParam_: function(paramValue) { var paramValueSplit = paramValue.split(','); - if ((paramValueSplit.length != 1) && (paramValueSplit.length != 3)) - return; + if (paramValueSplit.length != 1 && paramValueSplit.length != 3) + return {}; // User scale of 100 means zoom value of 100% i.e. zoom factor of 1.0. var zoomFactor = parseFloat(paramValueSplit[0]) / 100; if (isNaN(zoomFactor)) - return; + return {}; // Handle #zoom=scale. if (paramValueSplit.length == 1) { - viewportPosition['zoom'] = zoomFactor; - return; + return {'zoom': zoomFactor}; } // Handle #zoom=scale,left,top. @@ -46,8 +48,43 @@ OpenPDFParamsParser.prototype = { x: parseFloat(paramValueSplit[1]), y: parseFloat(paramValueSplit[2]) }; - viewportPosition['position'] = position; - viewportPosition['zoom'] = zoomFactor; + return {'position': position, 'zoom': zoomFactor}; + }, + + /** + * @private + * Parse view parameter of open PDF parameters. The PDF should be opened at + * the specified fitting type mode and position. + * @param {string} paramValue view value. + * @return {Object} Map with view parameters (view and viewPosition). + */ + parseViewParam_: function(paramValue) { + var viewModeComponents = paramValue.toLowerCase().split(','); + if (viewModeComponents.length < 1) + return {}; + + var params = {}; + var viewMode = viewModeComponents[0]; + var acceptsPositionParam; + if (viewMode === 'fit') { + params['view'] = FittingType.FIT_TO_PAGE; + acceptsPositionParam = false; + } else if (viewMode === 'fith') { + params['view'] = FittingType.FIT_TO_WIDTH; + acceptsPositionParam = true; + } else if (viewMode === 'fitv') { + params['view'] = FittingType.FIT_TO_HEIGHT; + acceptsPositionParam = true; + } + + if (!acceptsPositionParam || viewModeComponents.length < 2) + return params; + + var position = parseFloat(viewModeComponents[1]); + if (!isNaN(position)) + params['viewPosition'] = position; + + return params; }, /** @@ -108,28 +145,29 @@ OpenPDFParamsParser.prototype = { * @param {Function} callback function to be called with viewport info. */ getViewportFromUrlParams: function(url, callback) { - var viewportPosition = {}; - viewportPosition['url'] = url; + var params = {}; + params['url'] = url; - var paramsDictionary = this.parseUrlParams_(url); + var urlParams = this.parseUrlParams_(url); - if ('page' in paramsDictionary) { + if ('page' in urlParams) { // |pageNumber| is 1-based, but goToPage() take a zero-based page number. - var pageNumber = parseInt(paramsDictionary['page'], 10); + var pageNumber = parseInt(urlParams['page'], 10); if (!isNaN(pageNumber) && pageNumber > 0) - viewportPosition['page'] = pageNumber - 1; + params['page'] = pageNumber - 1; } - if ('zoom' in paramsDictionary) - this.parseZoomParam_(paramsDictionary['zoom'], viewportPosition); + if ('view' in urlParams) + Object.assign(params, this.parseViewParam_(urlParams['view'])); - if (viewportPosition.page === undefined && - 'nameddest' in paramsDictionary) { - this.outstandingRequests_.push( - {callback: callback, viewportPosition: viewportPosition}); - this.getNamedDestinationsFunction_(paramsDictionary['nameddest']); + if ('zoom' in urlParams) + Object.assign(params, this.parseZoomParam_(urlParams['zoom'])); + + if (params.page === undefined && 'nameddest' in urlParams) { + this.outstandingRequests_.push({callback: callback, params: params}); + this.getNamedDestinationsFunction_(urlParams['nameddest']); } else { - callback(viewportPosition); + callback(params); } }, @@ -142,7 +180,9 @@ OpenPDFParamsParser.prototype = { onNamedDestinationReceived: function(pageNumber) { var outstandingRequest = this.outstandingRequests_.shift(); if (pageNumber != -1) - outstandingRequest.viewportPosition.page = pageNumber; - outstandingRequest.callback(outstandingRequest.viewportPosition); + outstandingRequest.params.page = pageNumber; + outstandingRequest.callback(outstandingRequest.params); }, }; + +}()); diff --git a/chromium/chrome/browser/resources/pdf/pdf.js b/chromium/chrome/browser/resources/pdf/pdf.js index acb36ab187d..dcbf3d3a35e 100644 --- a/chromium/chrome/browser/resources/pdf/pdf.js +++ b/chromium/chrome/browser/resources/pdf/pdf.js @@ -187,8 +187,7 @@ function PDFViewer(browserApi) { // Setup the button event listeners. this.zoomToolbar_ = $('zoom-toolbar'); this.zoomToolbar_.addEventListener( - 'fit-to-width', this.viewport_.fitToWidth.bind(this.viewport_)); - this.zoomToolbar_.addEventListener('fit-to-page', this.fitToPage_.bind(this)); + 'fit-to-changed', this.fitToChanged_.bind(this)); this.zoomToolbar_.addEventListener( 'zoom-in', this.viewport_.zoomIn.bind(this.viewport_)); this.zoomToolbar_.addEventListener( @@ -196,7 +195,7 @@ function PDFViewer(browserApi) { this.gestureDetector_ = new GestureDetector(this.plugin_); this.gestureDetector_.addEventListener( - 'pinchstart', this.viewport_.pinchZoomStart.bind(this.viewport_)); + 'pinchstart', this.onPinchStart_.bind(this)); this.sentPinchEvent_ = false; this.gestureDetector_.addEventListener( 'pinchupdate', this.onPinchUpdate_.bind(this)); @@ -281,8 +280,8 @@ PDFViewer.prototype = { this.toolbarManager_.hideToolbarsAfterTimeout(e); var pageUpHandler = () => { - // Go to the previous page if we are fit-to-page. - if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) { + // Go to the previous page if we are fit-to-page or fit-to-height. + if (this.viewport_.isPagedMode()) { this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1); // Since we do the movement of the page. e.preventDefault(); @@ -292,8 +291,8 @@ PDFViewer.prototype = { } }; var pageDownHandler = () => { - // Go to the next page if we are fit-to-page. - if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) { + // Go to the next page if we are fit-to-page or fit-to-height. + if (this.viewport_.isPagedMode()) { this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1); // Since we do the movement of the page. e.preventDefault(); @@ -441,11 +440,19 @@ PDFViewer.prototype = { /** * @private - * Set zoom to "fit to page". + * Request to change the viewport fitting type. + * @param {CustomEvent} e Event received with the new FittingType as detail. */ - fitToPage_: function() { - this.viewport_.fitToPage(); - this.toolbarManager_.forceHideTopToolbar(); + fitToChanged_: function(e) { + if (e.detail == FittingType.FIT_TO_PAGE) { + this.viewport_.fitToPage(); + this.toolbarManager_.forceHideTopToolbar(); + } else if (e.detail == FittingType.FIT_TO_WIDTH) { + this.viewport_.fitToWidth(); + } else if (e.detail == FittingType.FIT_TO_HEIGHT) { + this.viewport_.fitToHeight(); + this.toolbarManager_.forceHideTopToolbar(); + } }, /** @@ -493,21 +500,37 @@ PDFViewer.prototype = { * Handle open pdf parameters. This function updates the viewport as per * the parameters mentioned in the url while opening pdf. The order is * important as later actions can override the effects of previous actions. - * @param {Object} viewportPosition The initial position of the viewport to be - * displayed. + * @param {Object} params The open params passed in the URL. */ - handleURLParams_: function(viewportPosition) { - if (viewportPosition.page != undefined) - this.viewport_.goToPage(viewportPosition.page); - if (viewportPosition.position) { + handleURLParams_: function(params) { + if (params.page != undefined) + this.viewport_.goToPage(params.page); + + if (params.position) { // Make sure we don't cancel effect of page parameter. this.viewport_.position = { - x: this.viewport_.position.x + viewportPosition.position.x, - y: this.viewport_.position.y + viewportPosition.position.y + x: this.viewport_.position.x + params.position.x, + y: this.viewport_.position.y + params.position.y }; } - if (viewportPosition.zoom) - this.viewport_.setZoom(viewportPosition.zoom); + + if (params.zoom) + this.viewport_.setZoom(params.zoom); + + if (params.view) { + this.isUserInitiatedEvent_ = false; + this.zoomToolbar_.forceFit(params.view); + if (params.viewPosition) { + var zoomedPositionShift = params.viewPosition * this.viewport_.zoom; + var currentViewportPosition = this.viewport_.position; + if (params.view == FittingType.FIT_TO_WIDTH) + currentViewportPosition.y += zoomedPositionShift; + else if (params.view == FittingType.FIT_TO_HEIGHT) + currentViewportPosition.x += zoomedPositionShift; + this.viewport_.position = currentViewportPosition; + } + this.isUserInitiatedEvent_ = true; + } }, /** @@ -660,6 +683,13 @@ PDFViewer.prototype = { case 'setIsSelecting': this.viewportScroller_.setEnableScrolling(message.data.isSelecting); break; + case 'setIsEditMode': + // TODO(hnakashima): Replace this with final visual indication from UX. + if (message.data.isEditMode) + this.toolbar_.docTitle = document.title + ' (edit mode)'; + else + this.toolbar_.docTitle = document.title; + break; case 'getNamedDestinationReply': this.paramsParser_.onNamedDestinationReceived(message.data.pageNumber); break; @@ -763,6 +793,19 @@ PDFViewer.prototype = { /** * @private + * A callback that's called when the start of a pinch zoom is detected. + * @param {!Object} e the pinch event. + */ + onPinchStart_: function(e) { + // We also use rAF for pinch start, so that if there is a pinch end event + // scheduled by rAF, this pinch start will be sent after. + window.requestAnimationFrame(() => { + this.viewport_.pinchZoomStart(e); + }); + }, + + /** + * @private * A callback that's called after the viewport changes. */ viewportChanged_: function() { @@ -874,7 +917,7 @@ PDFViewer.prototype = { if (!this.inPrintPreviewMode_) { this.inPrintPreviewMode_ = true; this.isUserInitiatedEvent_ = false; - this.zoomToolbar_.forceFitToPage(); + this.zoomToolbar_.forceFit(FittingType.FIT_TO_PAGE); this.isUserInitiatedEvent_ = true; } diff --git a/chromium/chrome/browser/resources/pdf/pdf_fitting_type.js b/chromium/chrome/browser/resources/pdf/pdf_fitting_type.js new file mode 100644 index 00000000000..41a14f09242 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/pdf_fitting_type.js @@ -0,0 +1,16 @@ +// 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. + +'use strict'; + +/** + * Enumeration of page fitting types. + * @enum {string} + */ +var FittingType = { + NONE: 'none', + FIT_TO_PAGE: 'fit-to-page', + FIT_TO_WIDTH: 'fit-to-width', + FIT_TO_HEIGHT: 'fit-to-height' +}; diff --git a/chromium/chrome/browser/resources/pdf/viewport.js b/chromium/chrome/browser/resources/pdf/viewport.js index e82551d1080..53e1b17ea60 100644 --- a/chromium/chrome/browser/resources/pdf/viewport.js +++ b/chromium/chrome/browser/resources/pdf/viewport.js @@ -16,15 +16,6 @@ function getIntersectionHeight(rect1, rect2) { } /** - * Makes sure that the scale level doesn't get out of the limits. - * @param {number} scale The new scale level. - * @return {number} The scale clamped within the limits. - */ -function clampScale(scale) { - return Math.min(5, Math.max(0.25, scale)); -} - -/** * Computes vector between two points. * @param {!Object} p1 The first point. * @param {!Object} p2 The second point. @@ -74,7 +65,7 @@ function Viewport( this.documentDimensions_ = null; this.pageDimensions_ = []; this.scrollbarWidth_ = scrollbarWidth; - this.fittingType_ = Viewport.FittingType.NONE; + this.fittingType_ = FittingType.NONE; this.defaultZoom_ = defaultZoom; this.topToolbarHeight_ = topToolbarHeight; this.prevScale_ = 1; @@ -88,16 +79,6 @@ function Viewport( } /** - * Enumeration of page fitting types. - * @enum {string} - */ -Viewport.FittingType = { - NONE: 'none', - FIT_TO_PAGE: 'fit-to-page', - FIT_TO_WIDTH: 'fit-to-width' -}; - -/** * Enumeration of pinch states. * This should match PinchPhase enum in pdf/out_of_process_instance.h * @enum {number} @@ -138,6 +119,17 @@ Viewport.ZOOM_FACTOR_RANGE = { }; /** + * Clamps the zoom factor (or page scale factor) to be within the limits. + * @param {number} factor The zoom/scale factor. + * @return {number} The factor clamped within the limits. + */ +Viewport.clampZoom = function(factor) { + return Math.max( + Viewport.ZOOM_FACTOR_RANGE.min, + Math.min(factor, Viewport.ZOOM_FACTOR_RANGE.max)); +}; + +/** * The width of the page shadow around pages in pixels. */ Viewport.PAGE_SHADOW = { @@ -240,10 +232,12 @@ Viewport.prototype = { * Called when the viewport size changes. */ resize_: function() { - if (this.fittingType_ == Viewport.FittingType.FIT_TO_PAGE) + if (this.fittingType_ == FittingType.FIT_TO_PAGE) this.fitToPageInternal_(false); - else if (this.fittingType_ == Viewport.FittingType.FIT_TO_WIDTH) + else if (this.fittingType_ == FittingType.FIT_TO_WIDTH) this.fitToWidth(); + else if (this.fittingType_ == FittingType.FIT_TO_HEIGHT) + this.fitToHeightInternal_(false); else this.updateViewport_(); }, @@ -369,7 +363,7 @@ Viewport.prototype = { this.allowedToChangeZoom_, 'Called Viewport.setPinchZoomInternal_ without calling ' + 'Viewport.mightZoom_.'); - this.internalZoom_ = clampScale(this.internalZoom_ * scaleDelta); + this.internalZoom_ = Viewport.clampZoom(this.internalZoom_ * scaleDelta); var newCenterInContent = this.frameToContent(center); var delta = { @@ -408,12 +402,9 @@ Viewport.prototype = { * @param {number} newZoom the zoom level to zoom to. */ setZoom: function(newZoom) { - this.fittingType_ = Viewport.FittingType.NONE; - newZoom = Math.max( - Viewport.ZOOM_FACTOR_RANGE.min, - Math.min(newZoom, Viewport.ZOOM_FACTOR_RANGE.max)); + this.fittingType_ = FittingType.NONE; this.mightZoom_(() => { - this.setZoomInternal_(newZoom); + this.setZoomInternal_(Viewport.clampZoom(newZoom)); this.updateViewport_(); }); }, @@ -449,7 +440,7 @@ Viewport.prototype = { }, /** - * @type {Viewport.FittingType} the fitting type the viewport is currently in. + * @type {FittingType} the fitting type the viewport is currently in. */ get fittingType() { return this.fittingType_; @@ -477,7 +468,8 @@ Viewport.prototype = { if (top <= y && bottom > y) return page; - else if (top > y) + + if (top > y) max = page - 1; else min = page + 1; @@ -516,25 +508,28 @@ Viewport.prototype = { /** * @private - * Compute the zoom level for fit-to-page or fit-to-width. |pageDimensions| is - * the dimensions for a given page and if |widthOnly| is true, it indicates - * that fit-to-page zoom should be computed rather than fit-to-page. - * @param {Object} pageDimensions the dimensions of a given page - * @param {boolean} widthOnly a bool indicating whether fit-to-page or - * fit-to-width should be computed. + * Compute the zoom level for fit-to-page, fit-to-width or fit-to-height. + * + * At least one of {fitWidth, fitHeight} must be true. + * + * @param {Object} pageDimensions the dimensions of a given page in px. + * @param {boolean} fitWidth a bool indicating whether the whole width of the + * page needs to be in the viewport. + * @param {boolean} fitHeight a bool indicating whether the whole height of + * the page needs to be in the viewport. * @return {number} the internal zoom to set */ - computeFittingZoom_: function(pageDimensions, widthOnly) { + computeFittingZoom_: function(pageDimensions, fitWidth, fitHeight) { + assert( + fitWidth || fitHeight, + 'Invalid parameters. At least one of fitWidth and fitHeight must be ' + + 'true.'); + // First compute the zoom without scrollbars. - var zoomWidth = this.window_.innerWidth / pageDimensions.width; - var zoom; - var zoomHeight; - if (widthOnly) { - zoom = zoomWidth; - } else { - zoomHeight = this.window_.innerHeight / pageDimensions.height; - zoom = Math.min(zoomWidth, zoomHeight); - } + var zoom = this.computeFittingZoomGivenDimensions_( + fitWidth, fitHeight, this.window_.innerWidth, this.window_.innerHeight, + pageDimensions.width, pageDimensions.height); + // Check if there needs to be any scrollbars. var needsScrollbars = this.documentNeedsScrollbars_(zoom); @@ -566,43 +561,110 @@ Viewport.prototype = { windowWithScrollbars.width -= scrollbarWidth; // Recompute the zoom. - zoomWidth = windowWithScrollbars.width / pageDimensions.width; - if (widthOnly) { - zoom = zoomWidth; - } else { - zoomHeight = windowWithScrollbars.height / pageDimensions.height; - zoom = Math.min(zoomWidth, zoomHeight); - } + zoom = this.computeFittingZoomGivenDimensions_( + fitWidth, fitHeight, windowWithScrollbars.width, + windowWithScrollbars.height, pageDimensions.width, + pageDimensions.height); + return this.zoomManager_.internalZoomComponent(zoom); }, /** - * Zoom the viewport so that the page-width consumes the entire viewport. + * @private + * Compute a zoom level given the dimensions to fit and the actual numbers + * in those dimensions. + * + * @param {boolean} fitWidth make sure the page width is totally contained in + * the window. + * @param {boolean} fitHeight make sure the page height is totally contained + * in the window. + * @param {number} windowWidth the width of the window in px. + * @param {number} windowHeight the height of the window in px. + * @param {number} pageWidth the width of the page in px. + * @param {number} pageHeight the height of the page in px. + * @return {number} the internal zoom to set + */ + computeFittingZoomGivenDimensions_: function( + fitWidth, fitHeight, windowWidth, windowHeight, pageWidth, pageHeight) { + // Assumes at least one of {fitWidth, fitHeight} is set. + var zoomWidth; + var zoomHeight; + + if (fitWidth) + zoomWidth = windowWidth / pageWidth; + + if (fitHeight) + zoomHeight = windowHeight / pageHeight; + + if (!fitWidth && fitHeight) + return zoomHeight; + if (fitWidth && !fitHeight) + return zoomWidth; + + // Assume fitWidth && fitHeight + return Math.min(zoomWidth, zoomHeight); + }, + + /** + * Zoom the viewport so that the page width consumes the entire viewport. */ fitToWidth: function() { this.mightZoom_(() => { - this.fittingType_ = Viewport.FittingType.FIT_TO_WIDTH; + this.fittingType_ = FittingType.FIT_TO_WIDTH; if (!this.documentDimensions_) return; // When computing fit-to-width, the maximum width of a page in the // document is used, which is equal to the size of the document width. this.setZoomInternal_( - this.computeFittingZoom_(this.documentDimensions_, true)); + this.computeFittingZoom_(this.documentDimensions_, true, false)); + this.updateViewport_(); + }); + }, + + /** + * @private + * Zoom the viewport so that the page height consumes the entire viewport. + * @param {boolean} scrollToTopOfPage Set to true if the viewport should be + * scrolled to the top of the current page. Set to false if the viewport + * should remain at the current scroll position. + */ + fitToHeightInternal_: function(scrollToTopOfPage) { + this.mightZoom_(() => { + this.fittingType_ = FittingType.FIT_TO_HEIGHT; + if (!this.documentDimensions_) + return; var page = this.getMostVisiblePage(); + // When computing fit-to-height, the maximum height of the current page + // is used. + var dimensions = { + width: 0, + height: this.pageDimensions_[page].height, + }; + this.setZoomInternal_(this.computeFittingZoom_(dimensions, false, true)); + if (scrollToTopOfPage) { + this.position = {x: 0, y: this.pageDimensions_[page].y * this.zoom}; + } this.updateViewport_(); }); }, /** + * Zoom the viewport so that the page height consumes the entire viewport. + */ + fitToHeight: function() { + this.fitToHeightInternal_(true); + }, + + /** * @private - * Zoom the viewport so that a page consumes the entire viewport. + * Zoom the viewport so that a page consumes as much as possible of the it. * @param {boolean} scrollToTopOfPage Set to true if the viewport should be * scrolled to the top of the current page. Set to false if the viewport * should remain at the current scroll position. */ fitToPageInternal_: function(scrollToTopOfPage) { this.mightZoom_(() => { - this.fittingType_ = Viewport.FittingType.FIT_TO_PAGE; + this.fittingType_ = FittingType.FIT_TO_PAGE; if (!this.documentDimensions_) return; var page = this.getMostVisiblePage(); @@ -611,7 +673,7 @@ Viewport.prototype = { width: this.documentDimensions_.width, height: this.pageDimensions_[page].height, }; - this.setZoomInternal_(this.computeFittingZoom_(dimensions, false)); + this.setZoomInternal_(this.computeFittingZoom_(dimensions, true, true)); if (scrollToTopOfPage) { this.position = {x: 0, y: this.pageDimensions_[page].y * this.zoom}; } @@ -632,7 +694,7 @@ Viewport.prototype = { */ zoomOut: function() { this.mightZoom_(() => { - this.fittingType_ = Viewport.FittingType.NONE; + this.fittingType_ = FittingType.NONE; var nextZoom = Viewport.ZOOM_FACTORS[0]; for (var i = 0; i < Viewport.ZOOM_FACTORS.length; i++) { if (Viewport.ZOOM_FACTORS[i] < this.internalZoom_) @@ -648,7 +710,7 @@ Viewport.prototype = { */ zoomIn: function() { this.mightZoom_(() => { - this.fittingType_ = Viewport.FittingType.NONE; + this.fittingType_ = FittingType.NONE; var nextZoom = Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1]; for (var i = Viewport.ZOOM_FACTORS.length - 1; i >= 0; i--) { if (Viewport.ZOOM_FACTORS[i] > this.internalZoom_) @@ -675,7 +737,7 @@ Viewport.prototype = { var needsScrollbars = this.documentNeedsScrollbars_(this.zoomManager_.applyBrowserZoom( - clampScale(this.internalZoom_ * scaleDelta))); + Viewport.clampZoom(this.internalZoom_ * scaleDelta))); this.pinchCenter_ = e.center; @@ -754,10 +816,10 @@ Viewport.prototype = { page = this.pageDimensions_.length - 1; var dimensions = this.pageDimensions_[page]; var toolbarOffset = 0; - // Unless we're in fit to page mode, scroll above the page by - // |this.topToolbarHeight_| so that the toolbar isn't covering it + // Unless we're in fit to page or fit to height mode, scroll above the + // page by |this.topToolbarHeight_| so that the toolbar isn't covering it // initially. - if (this.fittingType_ != Viewport.FittingType.FIT_TO_PAGE) + if (!this.isPagedMode()) toolbarOffset = this.topToolbarHeight_; this.position = { x: dimensions.x * this.zoom, @@ -779,7 +841,7 @@ Viewport.prototype = { if (initialDimensions) { this.setZoomInternal_(Math.min( this.defaultZoom_, - this.computeFittingZoom_(this.documentDimensions_, true))); + this.computeFittingZoom_(this.documentDimensions_, true, false))); this.position = {x: 0, y: -this.topToolbarHeight_}; } this.contentSizeChanged_(); @@ -829,5 +891,19 @@ Viewport.prototype = { width: insetDimensions.width * this.zoom, height: insetDimensions.height * this.zoom }; + }, + + /** + * Check if the current fitting type is a paged mode. + * + * In a paged mode, page up and page down scroll to the top of the + * previous/next page and part of the page is under the toolbar. + * + * @return {boolean} Whether the current fitting type is a paged mode. + */ + isPagedMode: function(page) { + return ( + this.fittingType_ == FittingType.FIT_TO_PAGE || + this.fittingType_ == FittingType.FIT_TO_HEIGHT); } }; diff --git a/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json b/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json index 99b6cf673f3..e9af78d3794 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": 26, + "x-version": 27, "google-talk": { "mime_types": [ ], @@ -80,9 +80,9 @@ ], "versions": [ { - "version": "27.0.0.187", + "version": "28.0.0.137", "status": "up_to_date", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb17-33.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb18-01.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 a85342825f9..6192e094ef0 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": 32, + "x-version": 33, "google-talk": { "mime_types": [ ], @@ -115,9 +115,9 @@ ], "versions": [ { - "version": "27.0.0.187", + "version": "28.0.0.137", "status": "requires_authorization", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb17-33.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb18-01.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 c5b325b08c2..9b5418e3fa5 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": 41, + "x-version": 42, "google-talk": { "mime_types": [ ], @@ -137,9 +137,9 @@ ], "versions": [ { - "version": "27.0.0.187", + "version": "28.0.0.137", "status": "requires_authorization", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb17-33.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb18-01.html" } ], "lang": "en-US", diff --git a/chromium/chrome/browser/resources/policy_tool.css b/chromium/chrome/browser/resources/policy_tool.css index 7361ccbd141..7ddad45a113 100644 --- a/chromium/chrome/browser/resources/policy_tool.css +++ b/chromium/chrome/browser/resources/policy_tool.css @@ -39,3 +39,7 @@ tbody.value-editing-on .value { #saving span { font-weight: bold; } + +#session-actions { + float: left; +} diff --git a/chromium/chrome/browser/resources/policy_tool.html b/chromium/chrome/browser/resources/policy_tool.html index 8af70271184..12b62273d24 100644 --- a/chromium/chrome/browser/resources/policy_tool.html +++ b/chromium/chrome/browser/resources/policy_tool.html @@ -51,6 +51,9 @@ </span> </section> <section id="sessions"> + <div id="session-actions"> + <button id="delete-session-button">-</button> + </div> <select size="4" id="session-list"> </select> </section> diff --git a/chromium/chrome/browser/resources/policy_tool.js b/chromium/chrome/browser/resources/policy_tool.js index 871322846d4..c3e321ce583 100644 --- a/chromium/chrome/browser/resources/policy_tool.js +++ b/chromium/chrome/browser/resources/policy_tool.js @@ -108,6 +108,14 @@ policy.Page.prototype.initialize = function() { this.enableEditing(); chrome.send('resetSession'); }; + + $('delete-session-button').onclick = () => { + var sessionName = $('session-list').value; + if (sessionName) { + chrome.send('deleteSession', [sessionName]); + } + }; + // 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'); @@ -255,18 +263,6 @@ policy.PolicyTable.prototype.getDictionary = function() { 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', diff --git a/chromium/chrome/browser/resources/print_preview/.eslintrc.js b/chromium/chrome/browser/resources/print_preview/.eslintrc.js new file mode 100644 index 00000000000..847d6e99509 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/.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/print_preview/cloud_print_interface.js b/chromium/chrome/browser/resources/print_preview/cloud_print_interface.js index 2527074c3ef..079d4ff5c83 100644 --- a/chromium/chrome/browser/resources/print_preview/cloud_print_interface.js +++ b/chromium/chrome/browser/resources/print_preview/cloud_print_interface.js @@ -24,154 +24,88 @@ cloudprint.CloudPrintInterfaceEventType = { cr.define('cloudprint', function() { 'use strict'; - var CloudPrintInterfaceEventType = cloudprint.CloudPrintInterfaceEventType; - - /** - * API to the Google Cloud Print service. - * @param {string} baseUrl Base part of the Google Cloud Print service URL - * with no trailing slash. For example, - * 'https://www.google.com/cloudprint'. - * @param {!print_preview.NativeLayer} nativeLayer Native layer used to get - * Auth2 tokens. - * @param {!print_preview.UserInfo} userInfo User information repository. - * @param {boolean} isInAppKioskMode Whether the print preview is in App - * Kiosk mode. - * @constructor - * @extends {cr.EventTarget} - */ - function CloudPrintInterface( - baseUrl, nativeLayer, userInfo, isInAppKioskMode) { - /** - * The base URL of the Google Cloud Print API. - * @type {string} - * @private - */ - this.baseUrl_ = baseUrl; - - /** - * Used to get Auth2 tokens. - * @type {!print_preview.NativeLayer} - * @private - */ - this.nativeLayer_ = nativeLayer; - - /** - * User information repository. - * @type {!print_preview.UserInfo} - * @private - */ - this.userInfo_ = userInfo; - - /** - * Whether Print Preview is in App Kiosk mode, basically, use only printers - * available for the device. - * @type {boolean} - * @private - */ - this.isInAppKioskMode_ = isInAppKioskMode; - - /** - * Currently logged in users (identified by email) mapped to the Google - * session index. - * @type {!Object<number>} - * @private - */ - this.userSessionIndex_ = {}; - - /** - * Stores last received XSRF tokens for each user account. Sent as - * a parameter with every request. - * @type {!Object<string>} - * @private - */ - this.xsrfTokens_ = {}; - - /** - * Outstanding cloud destination search requests. - * @type {!Array<!cloudprint.CloudPrintRequest>} - * @private - */ - this.outstandingCloudSearchRequests_ = []; - - /** - * Promise that will be resolved when the access token for - * DestinationOrigin.DEVICE is available. Null if there is no request - * currently pending. - * @private {?Promise<string>} - */ - this.accessTokenRequestPromise_ = null; - - } - - /** - * Content type header value for a URL encoded HTTP request. - * @type {string} - * @const - * @private - */ - CloudPrintInterface.URL_ENCODED_CONTENT_TYPE_ = - 'application/x-www-form-urlencoded'; - - /** - * Multi-part POST request boundary used in communication with Google - * Cloud Print. - * @type {string} - * @const - * @private - */ - CloudPrintInterface.MULTIPART_BOUNDARY_ = - '----CloudPrintFormBoundaryjc9wuprokl8i'; - - /** - * Content type header value for a multipart HTTP request. - * @type {string} - * @const - * @private - */ - CloudPrintInterface.MULTIPART_CONTENT_TYPE_ = - 'multipart/form-data; boundary=' + - CloudPrintInterface.MULTIPART_BOUNDARY_; - - /** - * Regex that extracts Chrome's version from the user-agent string. - * @type {!RegExp} - * @const - * @private - */ - CloudPrintInterface.VERSION_REGEXP_ = /.*Chrome\/([\d\.]+)/i; - - /** - * Enumeration of JSON response fields from Google Cloud Print API. - * @enum {string} - * @private - */ - CloudPrintInterface.JsonFields_ = {PRINTER: 'printer'}; - - /** - * Could Print origins used to search printers. - * @type {!Array<!print_preview.DestinationOrigin>} - * @const - * @private - */ - CloudPrintInterface.CLOUD_ORIGINS_ = [ - print_preview.DestinationOrigin.COOKIES, - print_preview.DestinationOrigin.DEVICE - ]; - - CloudPrintInterface.prototype = { - __proto__: cr.EventTarget.prototype, + const CloudPrintInterfaceEventType = cloudprint.CloudPrintInterfaceEventType; + + class CloudPrintInterface extends cr.EventTarget { + /** + * API to the Google Cloud Print service. + * @param {string} baseUrl Base part of the Google Cloud Print service URL + * with no trailing slash. For example, + * 'https://www.google.com/cloudprint'. + * @param {!print_preview.NativeLayer} nativeLayer Native layer used to get + * Auth2 tokens. + * @param {!print_preview.UserInfo} userInfo User information repository. + * @param {boolean} isInAppKioskMode Whether the print preview is in App + * Kiosk mode. + */ + constructor(baseUrl, nativeLayer, userInfo, isInAppKioskMode) { + super(); + + /** + * The base URL of the Google Cloud Print API. + * @private {string} + */ + this.baseUrl_ = baseUrl; + + /** + * Used to get Auth2 tokens. + * @private {!print_preview.NativeLayer} + */ + this.nativeLayer_ = nativeLayer; + + /** + * User information repository. + * @private {!print_preview.UserInfo} + */ + this.userInfo_ = userInfo; + + /** + * Whether Print Preview is in App Kiosk mode, basically, use only + * printers available for the device. + * @private {boolean} + */ + this.isInAppKioskMode_ = isInAppKioskMode; + + /** + * Currently logged in users (identified by email) mapped to the Google + * session index. + * @private {!Object<number>} + */ + this.userSessionIndex_ = {}; + + /** + * Stores last received XSRF tokens for each user account. Sent as + * a parameter with every request. + * @private {!Object<string>} + */ + this.xsrfTokens_ = {}; + + /** + * Outstanding cloud destination search requests. + * @private {!Array<!cloudprint.CloudPrintRequest>} + */ + this.outstandingCloudSearchRequests_ = []; + + /** + * Promise that will be resolved when the access token for + * DestinationOrigin.DEVICE is available. Null if there is no request + * currently pending. + * @private {?Promise<string>} + */ + this.accessTokenRequestPromise_ = null; + } /** @return {string} Base URL of the Google Cloud Print service. */ get baseUrl() { return this.baseUrl_; - }, + } /** * @return {boolean} Whether a search for cloud destinations is in progress. */ get isCloudDestinationSearchInProgress() { return this.outstandingCloudSearchRequests_.length > 0; - }, + } /** * Sends Google Cloud Print search API request. @@ -181,10 +115,9 @@ cr.define('cloudprint', function() { * searches destinations for {@code opt_origin} only, otherwise starts * searches for all origins. */ - search: function(opt_account, opt_origin) { - var account = opt_account || ''; - var origins = - opt_origin && [opt_origin] || CloudPrintInterface.CLOUD_ORIGINS_; + search(opt_account, opt_origin) { + const account = opt_account || ''; + let origins = opt_origin && [opt_origin] || CLOUD_ORIGINS_; if (this.isInAppKioskMode_) { origins = origins.filter(function(origin) { return origin != print_preview.DestinationOrigin.COOKIES; @@ -193,7 +126,7 @@ cr.define('cloudprint', function() { this.abortSearchRequests_(origins); this.search_(true, account, origins); this.search_(false, account, origins); - }, + } /** * Sends Google Cloud Print search API requests. @@ -206,8 +139,8 @@ cr.define('cloudprint', function() { * search printers for. * @private */ - search_: function(isRecent, account, origins) { - var params = [ + search_(isRecent, account, origins) { + const params = [ new HttpParam('connection_status', 'ALL'), new HttpParam('client', 'chrome'), new HttpParam('use_cdd', 'true') ]; @@ -215,34 +148,34 @@ cr.define('cloudprint', function() { params.push(new HttpParam('q', '^recent')); } origins.forEach(function(origin) { - var cpRequest = this.buildRequest_( + const cpRequest = this.buildRequest_( 'GET', 'search', params, origin, account, this.onSearchDone_.bind(this, isRecent)); this.outstandingCloudSearchRequests_.push(cpRequest); this.sendOrQueueRequest_(cpRequest); }, this); - }, + } /** * Sends Google Cloud Print printer sharing invitations API requests. * @param {string} account Account the request is sent for. */ - invites: function(account) { - var params = [ + invites(account) { + const params = [ new HttpParam('client', 'chrome'), ]; this.sendOrQueueRequest_(this.buildRequest_( 'GET', 'invites', params, print_preview.DestinationOrigin.COOKIES, account, this.onInvitesDone_.bind(this))); - }, + } /** * Accepts or rejects printer sharing invitation. * @param {!print_preview.Invitation} invitation Invitation to process. * @param {boolean} accept Whether to accept this invitation. */ - processInvite: function(invitation, accept) { - var params = [ + processInvite(invitation, accept) { + const params = [ new HttpParam('printerid', invitation.destination.id), new HttpParam('email', invitation.scopeId), new HttpParam('accept', accept ? 'true' : 'false'), @@ -252,7 +185,7 @@ cr.define('cloudprint', function() { 'POST', 'processinvite', params, invitation.destination.origin, invitation.destination.account, this.onProcessInviteDone_.bind(this, invitation, accept))); - }, + } /** * Sends a Google Cloud Print submit API request. @@ -263,14 +196,13 @@ cr.define('cloudprint', function() { * @param {!print_preview.DocumentInfo} documentInfo Document data model. * @param {string} data Base64 encoded data of the document. */ - submit: function(destination, printTicketStore, documentInfo, data) { - var result = - CloudPrintInterface.VERSION_REGEXP_.exec(navigator.userAgent); - var chromeVersion = 'unknown'; + submit(destination, printTicketStore, documentInfo, data) { + const result = VERSION_REGEXP_.exec(navigator.userAgent); + let chromeVersion = 'unknown'; if (result && result.length == 2) { chromeVersion = result[1]; } - var params = [ + const params = [ new HttpParam('printerid', destination.id), new HttpParam('contentType', 'dataUrl'), new HttpParam('title', documentInfo.title), @@ -280,11 +212,11 @@ cr.define('cloudprint', function() { new HttpParam('tag', '__google__chrome_version=' + chromeVersion), new HttpParam('tag', '__google__os=' + navigator.platform) ]; - var cpRequest = this.buildRequest_( + const cpRequest = this.buildRequest_( 'POST', 'submit', params, destination.origin, destination.account, this.onSubmitDone_.bind(this)); this.sendOrQueueRequest_(cpRequest); - }, + } /** * Sends a Google Cloud Print printer API request. @@ -296,15 +228,15 @@ cr.define('cloudprint', function() { * to get printer) and, if the active user account is not the one * requested, {@code account} is activated and printer request reissued. */ - printer: function(printerId, origin, account) { - var params = [ + printer(printerId, origin, account) { + const params = [ new HttpParam('printerid', printerId), new HttpParam('use_cdd', 'true'), new HttpParam('printer_connection_status', 'true') ]; this.sendOrQueueRequest_(this.buildRequest_( 'GET', 'printer', params, origin, account || '', this.onPrinterDone_.bind(this, printerId))); - }, + } /** * Builds request to the Google Cloud Print API. @@ -321,10 +253,10 @@ cr.define('cloudprint', function() { * @return {!cloudprint.CloudPrintRequest} Partially prepared request. * @private */ - buildRequest_: function(method, action, params, origin, account, callback) { - var url = this.baseUrl_ + '/' + action + '?xsrf='; + buildRequest_(method, action, params, origin, account, callback) { + let url = this.baseUrl_ + '/' + action + '?xsrf='; if (origin == print_preview.DestinationOrigin.COOKIES) { - var xsrfToken = this.xsrfTokens_[account]; + const xsrfToken = this.xsrfTokens_[account]; if (!xsrfToken) { // TODO(rltoscano): Should throw an error if not a read-only action or // issue an xsrf token request. @@ -332,13 +264,13 @@ cr.define('cloudprint', function() { url = url + xsrfToken; } if (account) { - var index = this.userSessionIndex_[account] || 0; + const index = this.userSessionIndex_[account] || 0; if (index > 0) { url += '&authuser=' + index; } } } - var body = null; + let body = null; if (params) { if (method == 'GET') { url = params.reduce(function(partialUrl, param) { @@ -349,29 +281,29 @@ cr.define('cloudprint', function() { body = params.reduce(function(partialBody, param) { return partialBody + 'Content-Disposition: form-data; name=\"' + param.name + '\"\r\n\r\n' + param.value + '\r\n--' + - CloudPrintInterface.MULTIPART_BOUNDARY_ + '\r\n'; - }, '--' + CloudPrintInterface.MULTIPART_BOUNDARY_ + '\r\n'); + MULTIPART_BOUNDARY_ + '\r\n'; + }, '--' + MULTIPART_BOUNDARY_ + '\r\n'); } } - var headers = {}; + const headers = {}; headers['X-CloudPrint-Proxy'] = 'ChromePrintPreview'; if (method == 'GET') { - headers['Content-Type'] = CloudPrintInterface.URL_ENCODED_CONTENT_TYPE_; + headers['Content-Type'] = URL_ENCODED_CONTENT_TYPE_; } else if (method == 'POST') { - headers['Content-Type'] = CloudPrintInterface.MULTIPART_CONTENT_TYPE_; + headers['Content-Type'] = MULTIPART_CONTENT_TYPE_; } - var xhr = new XMLHttpRequest(); + const xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.withCredentials = (origin == print_preview.DestinationOrigin.COOKIES); - for (var header in headers) { + for (const header in headers) { xhr.setRequestHeader(header, headers[header]); } return new cloudprint.CloudPrintRequest( xhr, body, origin, account, callback); - }, + } /** * Sends a request to the Google Cloud Print API or queues if it needs to @@ -379,7 +311,7 @@ cr.define('cloudprint', function() { * @param {!cloudprint.CloudPrintRequest} request Request to send or queue. * @private */ - sendOrQueueRequest_: function(request) { + sendOrQueueRequest_(request) { if (request.origin == print_preview.DestinationOrigin.COOKIES) { this.sendRequest_(request); return; @@ -392,18 +324,18 @@ cr.define('cloudprint', function() { this.accessTokenRequestPromise_.then( this.onAccessTokenReady_.bind(this, request)); - }, + } /** * Sends a request to the Google Cloud Print API. * @param {!cloudprint.CloudPrintRequest} request Request to send. * @private */ - sendRequest_: function(request) { + sendRequest_(request) { request.xhr.onreadystatechange = this.onReadyStateChange_.bind(this, request); request.xhr.send(request.body); - }, + } /** * Creates a Google Cloud Print interface error that is ready to dispatch. @@ -414,8 +346,8 @@ cr.define('cloudprint', function() { * @return {!Event} Google Cloud Print interface error event. * @private */ - createErrorEvent_: function(type, request) { - var errorEvent = new Event(type); + createErrorEvent_(type, request) { + const errorEvent = new Event(type); errorEvent.status = request.xhr.status; if (request.xhr.status == 200) { errorEvent.errorCode = request.result['errorCode']; @@ -426,7 +358,7 @@ cr.define('cloudprint', function() { } errorEvent.origin = request.origin; return errorEvent; - }, + } /** * Updates user info and session index from the {@code request} response. @@ -434,16 +366,16 @@ cr.define('cloudprint', function() { * info from. * @private */ - setUsers_: function(request) { + setUsers_(request) { if (request.origin == print_preview.DestinationOrigin.COOKIES) { - var users = request.result['request']['users'] || []; + const users = request.result['request']['users'] || []; this.userSessionIndex_ = {}; - for (var i = 0; i < users.length; i++) { + for (let i = 0; i < users.length; i++) { this.userSessionIndex_[users[i]] = i; } this.userInfo_.setUsers(request.result['request']['user'], users); } - }, + } /** * Terminates search requests for requested {@code origins}. @@ -451,7 +383,7 @@ cr.define('cloudprint', function() { * to terminate search requests for. * @private */ - abortSearchRequests_: function(origins) { + abortSearchRequests_(origins) { this.outstandingCloudSearchRequests_ = this.outstandingCloudSearchRequests_.filter(function(request) { if (origins.indexOf(request.origin) >= 0) { @@ -460,7 +392,7 @@ cr.define('cloudprint', function() { } return true; }); - }, + } /** * Called when a native layer receives access token. Assumes that the @@ -470,7 +402,7 @@ cr.define('cloudprint', function() { * @param {string} accessToken The access token obtained. * @private */ - onAccessTokenReady_: function(request, accessToken) { + onAccessTokenReady_(request, accessToken) { assert(request.origin == print_preview.DestinationOrigin.DEVICE); if (accessToken) { request.xhr.setRequestHeader( @@ -482,7 +414,7 @@ cr.define('cloudprint', function() { request.callback(request); } this.accessTokenRequestPromise_ = null; - }, + } /** * Called when the ready-state of a XML http request changes. @@ -490,7 +422,7 @@ cr.define('cloudprint', function() { * @param {!cloudprint.CloudPrintRequest} request Request that was changed. * @private */ - onReadyStateChange_: function(request) { + onReadyStateChange_(request) { if (request.xhr.readyState == 4) { if (request.xhr.status == 200) { request.result = @@ -501,10 +433,9 @@ cr.define('cloudprint', function() { request.result['xsrf_token']; } } - request.status = request.xhr.status; request.callback(request); } - }, + } /** * Called when the search request completes. @@ -514,8 +445,8 @@ cr.define('cloudprint', function() { * completed. * @private */ - onSearchDone_: function(isRecent, request) { - var lastRequestForThisOrigin = true; + onSearchDone_(isRecent, request) { + let lastRequestForThisOrigin = true; this.outstandingCloudSearchRequests_ = this.outstandingCloudSearchRequests_.filter(function(item) { if (item != request && item.origin == request.origin) { @@ -523,19 +454,19 @@ cr.define('cloudprint', function() { } return item != request; }); - var activeUser = ''; + let activeUser = ''; if (request.origin == print_preview.DestinationOrigin.COOKIES) { activeUser = request.result && request.result['request'] && request.result['request']['user']; } - var event = null; + let event = null; if (request.xhr.status == 200 && request.result['success']) { // Extract printers. - var printerListJson = request.result['printers'] || []; - var printerList = []; + const printerListJson = request.result['printers'] || []; + const printerList = []; printerListJson.forEach(function(printerJson) { try { - printerList.push(cloudprint.CloudDestinationParser.parse( + printerList.push(cloudprint.parseCloudDestination( printerJson, request.origin, activeUser)); } catch (err) { console.error('Unable to parse cloud print destination: ' + err); @@ -555,7 +486,7 @@ cr.define('cloudprint', function() { event.user = activeUser; event.searchDone = lastRequestForThisOrigin; this.dispatchEvent(event); - }, + } /** * Called when invitations search request completes. @@ -563,19 +494,19 @@ cr.define('cloudprint', function() { * completed. * @private */ - onInvitesDone_: function(request) { - var event = null; - var activeUser = (request.result && request.result['request'] && - request.result['request']['user']) || + onInvitesDone_(request) { + let event = null; + const activeUser = (request.result && request.result['request'] && + request.result['request']['user']) || ''; if (request.xhr.status == 200 && request.result['success']) { // Extract invitations. - var invitationListJson = request.result['invites'] || []; - var invitationList = []; + const invitationListJson = request.result['invites'] || []; + const invitationList = []; invitationListJson.forEach(function(invitationJson) { try { invitationList.push( - cloudprint.InvitationParser.parse(invitationJson, activeUser)); + cloudprint.parseInvitation(invitationJson, activeUser)); } catch (e) { console.error('Unable to parse invitation: ' + e); } @@ -589,7 +520,7 @@ cr.define('cloudprint', function() { } event.user = activeUser; this.dispatchEvent(event); - }, + } /** * Called when invitation processing request completes. @@ -599,16 +530,16 @@ cr.define('cloudprint', function() { * completed. * @private */ - onProcessInviteDone_: function(invitation, accept, request) { - var event = null; - var activeUser = (request.result && request.result['request'] && - request.result['request']['user']) || + onProcessInviteDone_(invitation, accept, request) { + let event = null; + const activeUser = (request.result && request.result['request'] && + request.result['request']['user']) || ''; if (request.xhr.status == 200 && request.result['success']) { event = new Event(CloudPrintInterfaceEventType.PROCESS_INVITE_DONE); if (accept) { try { - event.printer = cloudprint.CloudDestinationParser.parse( + event.printer = cloudprint.parseCloudDestination( request.result['printer'], request.origin, activeUser); } catch (e) { console.error('Failed to parse cloud print destination: ' + e); @@ -622,7 +553,7 @@ cr.define('cloudprint', function() { event.accept = accept; event.user = activeUser; this.dispatchEvent(event); - }, + } /** * Called when the submit request completes. @@ -630,18 +561,18 @@ cr.define('cloudprint', function() { * completed. * @private */ - onSubmitDone_: function(request) { + onSubmitDone_(request) { if (request.xhr.status == 200 && request.result['success']) { - var submitDoneEvent = + const submitDoneEvent = new Event(CloudPrintInterfaceEventType.SUBMIT_DONE); submitDoneEvent.jobId = request.result['job']['id']; this.dispatchEvent(submitDoneEvent); } else { - var errorEvent = this.createErrorEvent_( + const errorEvent = this.createErrorEvent_( CloudPrintInterfaceEventType.SUBMIT_FAILED, request); this.dispatchEvent(errorEvent); } - }, + } /** * Called when the printer request completes. @@ -650,7 +581,7 @@ cr.define('cloudprint', function() { * completed. * @private */ - onPrinterDone_: function(destinationId, request) { + onPrinterDone_(destinationId, request) { // Special handling of the first printer request. It does not matter at // this point, whether printer was found or not. if (request.origin == print_preview.DestinationOrigin.COOKIES && @@ -673,14 +604,14 @@ cr.define('cloudprint', function() { } // Process response. if (request.xhr.status == 200 && request.result['success']) { - var activeUser = ''; + let activeUser = ''; if (request.origin == print_preview.DestinationOrigin.COOKIES) { activeUser = request.result['request']['user']; } - var printerJson = request.result['printers'][0]; - var printer; + const printerJson = request.result['printers'][0]; + let printer; try { - printer = cloudprint.CloudDestinationParser.parse( + printer = cloudprint.parseCloudDestination( printerJson, request.origin, activeUser); } catch (err) { console.error( @@ -688,93 +619,135 @@ cr.define('cloudprint', function() { JSON.stringify(printerJson)); return; } - var printerDoneEvent = + const printerDoneEvent = new Event(CloudPrintInterfaceEventType.PRINTER_DONE); printerDoneEvent.printer = printer; this.dispatchEvent(printerDoneEvent); } else { - var errorEvent = this.createErrorEvent_( + const errorEvent = this.createErrorEvent_( CloudPrintInterfaceEventType.PRINTER_FAILED, request); errorEvent.destinationId = destinationId; errorEvent.destinationOrigin = request.origin; this.dispatchEvent(errorEvent); } - }, - }; + } + } /** - * Data structure that holds data for Cloud Print requests. - * @param {!XMLHttpRequest} xhr Partially prepared http request. - * @param {string} body Data to send with POST requests. - * @param {!print_preview.DestinationOrigin} origin Origin for destination. - * @param {?string} account Account the request is sent for. Can be - * {@code null} or empty string if the request is not cookie bound or - * is sent on behalf of the primary user. - * @param {function(!cloudprint.CloudPrintRequest)} callback Callback to - * invoke when request completes. - * @constructor + * Content type header value for a URL encoded HTTP request. + * @const {string} + * @private */ - function CloudPrintRequest(xhr, body, origin, account, callback) { - /** - * Partially prepared http request. - * @type {!XMLHttpRequest} - */ - this.xhr = xhr; - - /** - * Data to send with POST requests. - * @type {string} - */ - this.body = body; - - /** - * Origin for destination. - * @type {!print_preview.DestinationOrigin} - */ - this.origin = origin; + const URL_ENCODED_CONTENT_TYPE_ = 'application/x-www-form-urlencoded'; - /** - * User account this request is expected to be executed for. - * @type {?string} - */ - this.account = account; + /** + * Multi-part POST request boundary used in communication with Google + * Cloud Print. + * @const {string} + * @private + */ + const MULTIPART_BOUNDARY_ = '----CloudPrintFormBoundaryjc9wuprokl8i'; - /** - * Callback to invoke when request completes. - * @type {function(!cloudprint.CloudPrintRequest)} - */ - this.callback = callback; + /** + * Content type header value for a multipart HTTP request. + * @const {string} + * @private + */ + const MULTIPART_CONTENT_TYPE_ = + 'multipart/form-data; boundary=' + MULTIPART_BOUNDARY_; - /** - * Result for requests. - * @type {Object} JSON response. - */ - this.result = null; - } + /** + * Regex that extracts Chrome's version from the user-agent string. + * @const {!RegExp} + * @private + */ + const VERSION_REGEXP_ = /.*Chrome\/([\d\.]+)/i; /** - * Data structure that represents an HTTP parameter. - * @param {string} name Name of the parameter. - * @param {string} value Value of the parameter. - * @constructor + * Could Print origins used to search printers. + * @const {!Array<!print_preview.DestinationOrigin>} + * @private */ - function HttpParam(name, value) { - /** - * Name of the parameter. - * @type {string} - */ - this.name = name; + const CLOUD_ORIGINS_ = [ + print_preview.DestinationOrigin.COOKIES, + print_preview.DestinationOrigin.DEVICE + ]; + class CloudPrintRequest { /** - * Name of the value. - * @type {string} + * Data structure that holds data for Cloud Print requests. + * @param {!XMLHttpRequest} xhr Partially prepared http request. + * @param {string} body Data to send with POST requests. + * @param {!print_preview.DestinationOrigin} origin Origin for destination. + * @param {?string} account Account the request is sent for. Can be + * {@code null} or empty string if the request is not cookie bound or + * is sent on behalf of the primary user. + * @param {function(!cloudprint.CloudPrintRequest)} callback Callback to + * invoke when request completes. */ - this.value = value; + constructor(xhr, body, origin, account, callback) { + /** + * Partially prepared http request. + * @type {!XMLHttpRequest} + */ + this.xhr = xhr; + + /** + * Data to send with POST requests. + * @type {string} + */ + this.body = body; + + /** + * Origin for destination. + * @type {!print_preview.DestinationOrigin} + */ + this.origin = origin; + + /** + * User account this request is expected to be executed for. + * @type {?string} + */ + this.account = account; + + /** + * Callback to invoke when request completes. + * @type {function(!cloudprint.CloudPrintRequest)} + */ + this.callback = callback; + + /** + * Result for requests. + * @type {Object} JSON response. + */ + this.result = null; + } + } + + class HttpParam { + /** + * Data structure that represents an HTTP parameter. + * @param {string} name Name of the parameter. + * @param {string} value Value of the parameter. + */ + constructor(name, value) { + /** + * Name of the parameter. + * @type {string} + */ + this.name = name; + + /** + * Name of the value. + * @type {string} + */ + this.value = value; + } } // Export return { CloudPrintInterface: CloudPrintInterface, - CloudPrintRequest: CloudPrintRequest + CloudPrintRequest: CloudPrintRequest, }; }); diff --git a/chromium/chrome/browser/resources/print_preview/common/overlay.js b/chromium/chrome/browser/resources/print_preview/common/overlay.js index 2680aad2853..94e29e43411 100644 --- a/chromium/chrome/browser/resources/print_preview/common/overlay.js +++ b/chromium/chrome/browser/resources/print_preview/common/overlay.js @@ -36,7 +36,7 @@ cr.define('print_preview', function() { e.preventDefault(); this.cancel(); } else if (e.keyCode == 13) { - var activeElementTag = document.activeElement ? + const activeElementTag = document.activeElement ? document.activeElement.tagName.toUpperCase() : ''; if (activeElementTag != 'BUTTON' && activeElementTag != 'SELECT') { diff --git a/chromium/chrome/browser/resources/print_preview/common/search_box.js b/chromium/chrome/browser/resources/print_preview/common/search_box.js index d8db692974f..859c99977cf 100644 --- a/chromium/chrome/browser/resources/print_preview/common/search_box.js +++ b/chromium/chrome/browser/resources/print_preview/common/search_box.js @@ -96,12 +96,12 @@ cr.define('print_preview', function() { */ dispatchSearchEvent_: function() { this.timeout_ = null; - var searchEvent = new Event(SearchBox.EventType.SEARCH); - var query = this.getQuery_(); + const searchEvent = new Event(SearchBox.EventType.SEARCH); + const query = this.getQuery_(); searchEvent.query = query; if (query) { // Generate regexp-safe query by escaping metacharacters. - var safeQuery = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + const safeQuery = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); searchEvent.queryRegExp = new RegExp('(' + safeQuery + ')', 'ig'); } else { searchEvent.queryRegExp = null; diff --git a/chromium/chrome/browser/resources/print_preview/common/search_bubble.js b/chromium/chrome/browser/resources/print_preview/common/search_bubble.js index ab78aa57d93..ff64969a55d 100644 --- a/chromium/chrome/browser/resources/print_preview/common/search_bubble.js +++ b/chromium/chrome/browser/resources/print_preview/common/search_bubble.js @@ -11,7 +11,7 @@ cr.define('print_preview', function() { * @extends {HTMLDivElement} */ function SearchBubble(text) { - var el = cr.doc.createElement('div'); + const el = cr.doc.createElement('div'); SearchBubble.decorate(el); el.content = text; return el; @@ -49,7 +49,7 @@ cr.define('print_preview', function() { /** Attach the bubble to the element. */ attachTo: function(element) { - var parent = element.parentElement; + const parent = element.parentElement; if (!parent) return; if (parent.tagName == 'TD') { @@ -72,8 +72,8 @@ cr.define('print_preview', function() { dispose: function() { clearInterval(this.intervalId); - var child = this.wrapper || this; - var parent = child.parentNode; + const child = this.wrapper || this; + const parent = child.parentNode; if (parent) parent.removeChild(child); }, @@ -84,16 +84,16 @@ cr.define('print_preview', function() { */ updatePosition: function() { // This bubble is 'owned' by the next sibling. - var owner = (this.wrapper || this).nextSibling; + const owner = (this.wrapper || this).nextSibling; // If there isn't an offset parent, we have nothing to do. if (!owner.offsetParent) return; // Position the bubble below the location of the owner. - var left = + const left = owner.offsetLeft + owner.offsetWidth / 2 - this.offsetWidth / 2; - var top = owner.offsetTop + owner.offsetHeight; + const top = owner.offsetTop + owner.offsetHeight; // Update the position in the CSS. Cache the last values for // best performance. diff --git a/chromium/chrome/browser/resources/print_preview/compiled_resources2.gyp b/chromium/chrome/browser/resources/print_preview/compiled_resources2.gyp index 3f740401d6d..854ca2d0bd8 100644 --- a/chromium/chrome/browser/resources/print_preview/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/print_preview/compiled_resources2.gyp @@ -25,6 +25,15 @@ ], }, 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - } + }, + { + 'target_name': 'print_preview_utils', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + 'data/compiled_resources2.gyp:size', + 'data/compiled_resources2.gyp:coordinate2d' + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, ], } diff --git a/chromium/chrome/browser/resources/print_preview/component.js b/chromium/chrome/browser/resources/print_preview/component.js index 02109b85b7f..00227848188 100644 --- a/chromium/chrome/browser/resources/print_preview/component.js +++ b/chromium/chrome/browser/resources/print_preview/component.js @@ -148,7 +148,7 @@ cr.define('print_preview', function() { * component's children. */ removeChild: function(child) { - var childIdx = this.children_.indexOf(child); + const childIdx = this.children_.indexOf(child); if (childIdx != -1) { this.children_.splice(childIdx, 1); } @@ -202,10 +202,10 @@ cr.define('print_preview', function() { * @protected */ cloneTemplateInternal: function(templateId, opt_keepHidden) { - var templateEl = $(templateId); + const templateEl = $(templateId); assert( templateEl != null, 'Could not find element with ID: ' + templateId); - var el = assertInstanceof(templateEl.cloneNode(true), HTMLElement); + const el = assertInstanceof(templateEl.cloneNode(true), HTMLElement); el.id = ''; if (!opt_keepHidden) { setIsVisible(el, true); 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 6e14236ee9c..68d75b878f7 100644 --- a/chromium/chrome/browser/resources/print_preview/data/app_state.js +++ b/chromium/chrome/browser/resources/print_preview/data/app_state.js @@ -46,13 +46,13 @@ print_preview.AppStateRecentDestination; */ function makeRecentDestination(destination) { return { - id: destination.id_, - origin: destination.origin_, - account: destination.account_ || '', + id: destination.id, + origin: destination.origin, + account: destination.account || '', capabilities: destination.capabilities, - displayName: destination.displayName_ || '', - extensionId: destination.extensionId_ || '', - extensionName: destination.extensionName_ || '', + displayName: destination.displayName || '', + extensionId: destination.extensionId || '', + extensionName: destination.extensionName || '', }; } @@ -65,6 +65,8 @@ cr.define('print_preview', function() { constructor() { /** * Internal representation of application state. + * Must contain only plain objects or classes that override the + * toJSON() method. * @private {!Object} */ this.state_ = {}; @@ -100,7 +102,7 @@ cr.define('print_preview', function() { * @return {boolean} Whether the selected destination is valid. */ isSelectedDestinationValid() { - var selected = this.selectedDestination; + const selected = this.selectedDestination; return !!selected && !!selected.id && !!selected.origin; } @@ -143,7 +145,7 @@ cr.define('print_preview', function() { init(serializedAppStateStr) { if (serializedAppStateStr) { try { - var state = JSON.parse(serializedAppStateStr); + const state = JSON.parse(serializedAppStateStr); if (!!state && state[print_preview.AppStateField.VERSION] == AppState.VERSION_) { this.state_ = /** @type {!Object} */ (state); @@ -161,7 +163,8 @@ cr.define('print_preview', function() { } else if (!(this.state_[print_preview.AppStateField .RECENT_DESTINATIONS] instanceof Array)) { - var tmp = this.state_[print_preview.AppStateField.RECENT_DESTINATIONS]; + const tmp = + this.state_[print_preview.AppStateField.RECENT_DESTINATIONS]; this.state_[print_preview.AppStateField.RECENT_DESTINATIONS] = [tmp]; } else if ( !this.state_[print_preview.AppStateField.RECENT_DESTINATIONS][0] || @@ -209,8 +212,8 @@ cr.define('print_preview', function() { // Determine if this destination is already in the recent destinations, // and where in the array it is located. - var newDestination = makeRecentDestination(dest); - var indexFound = + const newDestination = makeRecentDestination(dest); + let indexFound = this.state_[print_preview.AppStateField.RECENT_DESTINATIONS] .findIndex(function(recent) { return ( @@ -219,7 +222,9 @@ cr.define('print_preview', function() { }); // No change - if (indexFound == 0) { + if (indexFound == 0 && + this.selectedDestination.capabilities == + newDestination.capabilities) { this.persist_(); return; } diff --git a/chromium/chrome/browser/resources/print_preview/data/cloud_parsers.js b/chromium/chrome/browser/resources/print_preview/data/cloud_parsers.js index 25e16c1e4e1..8d8f043adad 100644 --- a/chromium/chrome/browser/resources/print_preview/data/cloud_parsers.js +++ b/chromium/chrome/browser/resources/print_preview/data/cloud_parsers.js @@ -5,15 +5,11 @@ cr.define('cloudprint', function() { 'use strict'; - /** Namespace which contains a method to parse cloud destinations directly. */ - function CloudDestinationParser() {} - /** * Enumeration of cloud destination field names. * @enum {string} - * @private */ - CloudDestinationParser.Field_ = { + const CloudDestinationField = { CAPABILITIES: 'capabilities', CONNECTION_STATUS: 'connectionStatus', DESCRIPTION: 'description', @@ -26,32 +22,45 @@ cr.define('cloudprint', function() { /** * Special tag that denotes whether the destination has been recently used. - * @type {string} - * @const - * @private + * @const {string} */ - CloudDestinationParser.RECENT_TAG_ = '^recent'; + const RECENT_TAG = '^recent'; /** * Special tag that denotes whether the destination is owned by the user. - * @type {string} - * @const - * @private + * @const {string} */ - CloudDestinationParser.OWNED_TAG_ = '^own'; + const OWNED_TAG = '^own'; /** * Enumeration of cloud destination types that are supported by print preview. * @enum {string} - * @private */ - CloudDestinationParser.CloudType_ = { + const DestinationCloudType = { ANDROID: 'ANDROID_CHROME_SNAPSHOT', DOCS: 'DOCS', IOS: 'IOS_CHROME_SNAPSHOT' }; /** + * Parses the destination type. + * @param {string} typeStr Destination type given by the Google Cloud Print + * server. + * @return {!print_preview.DestinationType} Destination type. + * @private + */ + function parseType(typeStr) { + if (typeStr == DestinationCloudType.ANDROID || + typeStr == DestinationCloudType.IOS) { + return print_preview.DestinationType.MOBILE; + } + if (typeStr == DestinationCloudType.DOCS) { + return print_preview.DestinationType.GOOGLE_PROMOTED; + } + return print_preview.DestinationType.GOOGLE; + } + + /** * Parses a destination from JSON from a Google Cloud Print search or printer * response. * @param {!Object} json Object that represents a Google Cloud Print search or @@ -62,76 +71,52 @@ cr.define('cloudprint', function() { * empty string, if origin != COOKIES. * @return {!print_preview.Destination} Parsed destination. */ - CloudDestinationParser.parse = function(json, origin, account) { - if (!json.hasOwnProperty(CloudDestinationParser.Field_.ID) || - !json.hasOwnProperty(CloudDestinationParser.Field_.TYPE) || - !json.hasOwnProperty(CloudDestinationParser.Field_.DISPLAY_NAME)) { + function parseCloudDestination(json, origin, account) { + if (!json.hasOwnProperty(CloudDestinationField.ID) || + !json.hasOwnProperty(CloudDestinationField.TYPE) || + !json.hasOwnProperty(CloudDestinationField.DISPLAY_NAME)) { throw Error('Cloud destination does not have an ID or a display name'); } - var id = json[CloudDestinationParser.Field_.ID]; - var tags = json[CloudDestinationParser.Field_.TAGS] || []; - var connectionStatus = - json[CloudDestinationParser.Field_.CONNECTION_STATUS] || + const id = json[CloudDestinationField.ID]; + const tags = json[CloudDestinationField.TAGS] || []; + const connectionStatus = json[CloudDestinationField.CONNECTION_STATUS] || print_preview.DestinationConnectionStatus.UNKNOWN; - var optionalParams = { + const optionalParams = { account: account, tags: tags, - isOwned: arrayContains(tags, CloudDestinationParser.OWNED_TAG_), + isOwned: arrayContains(tags, OWNED_TAG), lastAccessTime: - parseInt(json[CloudDestinationParser.Field_.LAST_ACCESS], 10) || - Date.now(), + parseInt(json[CloudDestinationField.LAST_ACCESS], 10) || Date.now(), cloudID: id, - description: json[CloudDestinationParser.Field_.DESCRIPTION] + description: json[CloudDestinationField.DESCRIPTION] }; - var cloudDest = new print_preview.Destination( - id, - CloudDestinationParser.parseType_( - json[CloudDestinationParser.Field_.TYPE]), - origin, json[CloudDestinationParser.Field_.DISPLAY_NAME], - arrayContains(tags, CloudDestinationParser.RECENT_TAG_) /*isRecent*/, - connectionStatus, optionalParams); - if (json.hasOwnProperty(CloudDestinationParser.Field_.CAPABILITIES)) { + const cloudDest = new print_preview.Destination( + id, parseType(json[CloudDestinationField.TYPE]), origin, + json[CloudDestinationField.DISPLAY_NAME], + arrayContains(tags, RECENT_TAG) /*isRecent*/, connectionStatus, + optionalParams); + if (json.hasOwnProperty(CloudDestinationField.CAPABILITIES)) { cloudDest.capabilities = /** @type {!print_preview.Cdd} */ ( - json[CloudDestinationParser.Field_.CAPABILITIES]); + json[CloudDestinationField.CAPABILITIES]); } return cloudDest; - }; - - /** - * Parses the destination type. - * @param {string} typeStr Destination type given by the Google Cloud Print - * server. - * @return {!print_preview.DestinationType} Destination type. - * @private - */ - CloudDestinationParser.parseType_ = function(typeStr) { - if (typeStr == CloudDestinationParser.CloudType_.ANDROID || - typeStr == CloudDestinationParser.CloudType_.IOS) { - return print_preview.DestinationType.MOBILE; - } - if (typeStr == CloudDestinationParser.CloudType_.DOCS) { - return print_preview.DestinationType.GOOGLE_PROMOTED; - } - return print_preview.DestinationType.GOOGLE; - }; - - /** Namespace which contains a method to parse printer sharing invitation. */ - function InvitationParser() {} + } /** * Enumeration of invitation field names. * @enum {string} - * @private */ - InvitationParser - .Field_ = {PRINTER: 'printer', RECEIVER: 'receiver', SENDER: 'sender'}; + const InvitationField = { + PRINTER: 'printer', + RECEIVER: 'receiver', + SENDER: 'sender' + }; /** * Enumeration of cloud destination types that are supported by print preview. * @enum {string} - * @private */ - InvitationParser.AclType_ = + const InvitationAclType = {DOMAIN: 'DOMAIN', GROUP: 'GROUP', PUBLIC: 'PUBLIC', USER: 'USER'}; /** @@ -140,44 +125,44 @@ cr.define('cloudprint', function() { * @param {string} account The account this invitation is sent for. * @return {!print_preview.Invitation} Parsed invitation. */ - InvitationParser.parse = function(json, account) { - if (!json.hasOwnProperty(InvitationParser.Field_.SENDER) || - !json.hasOwnProperty(InvitationParser.Field_.RECEIVER) || - !json.hasOwnProperty(InvitationParser.Field_.PRINTER)) { + function parseInvitation(json, account) { + if (!json.hasOwnProperty(InvitationField.SENDER) || + !json.hasOwnProperty(InvitationField.RECEIVER) || + !json.hasOwnProperty(InvitationField.PRINTER)) { throw Error('Invitation does not have necessary info.'); } - var nameFormatter = function(name, scope) { + const nameFormatter = function(name, scope) { return name && scope ? (name + ' (' + scope + ')') : (name || scope); }; - var sender = json[InvitationParser.Field_.SENDER]; - var senderName = nameFormatter(sender['name'], sender['email']); + const sender = json[InvitationField.SENDER]; + const senderName = nameFormatter(sender['name'], sender['email']); - var receiver = json[InvitationParser.Field_.RECEIVER]; - var receiverName = ''; - var receiverType = receiver['type']; - if (receiverType == InvitationParser.AclType_.USER) { + const receiver = json[InvitationField.RECEIVER]; + let receiverName = ''; + const receiverType = receiver['type']; + if (receiverType == InvitationAclType.USER) { // It's a personal invitation, empty name indicates just that. } else if ( - receiverType == InvitationParser.AclType_.GROUP || - receiverType == InvitationParser.AclType_.DOMAIN) { + receiverType == InvitationAclType.GROUP || + receiverType == InvitationAclType.DOMAIN) { receiverName = nameFormatter(receiver['name'], receiver['scope']); } else { throw Error('Invitation of unsupported receiver type'); } - var destination = cloudprint.CloudDestinationParser.parse( - json[InvitationParser.Field_.PRINTER], - print_preview.DestinationOrigin.COOKIES, account); + const destination = cloudprint.parseCloudDestination( + json[InvitationField.PRINTER], print_preview.DestinationOrigin.COOKIES, + account); return new print_preview.Invitation( senderName, receiverName, destination, receiver, account); - }; + } // Export return { - CloudDestinationParser: CloudDestinationParser, - InvitationParser: InvitationParser + parseCloudDestination: parseCloudDestination, + parseInvitation: parseInvitation, }; }); diff --git a/chromium/chrome/browser/resources/print_preview/data/compiled_resources2.gyp b/chromium/chrome/browser/resources/print_preview/data/compiled_resources2.gyp new file mode 100644 index 00000000000..d69be6dfbcb --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/data/compiled_resources2.gyp @@ -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. +{ + 'targets': [ + { + 'target_name': 'destination', + 'dependencies': [ + '../compiled_resources2.gyp:print_preview_utils', + '<(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': 'document_info', + 'dependencies': [ + 'margins', + 'size', + 'coordinate2d', + 'printable_area', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/cr/compiled_resources2.gyp:event_target', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'margins', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'printable_area', + 'dependencies': [ + 'size', + 'coordinate2d', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'size', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'coordinate2d', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ] +} diff --git a/chromium/chrome/browser/resources/print_preview/data/coordinate2d.html b/chromium/chrome/browser/resources/print_preview/data/coordinate2d.html new file mode 100644 index 00000000000..a121f0962c8 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/data/coordinate2d.html @@ -0,0 +1,3 @@ +<link rel="import" href="chrome://resources/html/cr.html"> + +<script src="coordinate2d.js"></script> diff --git a/chromium/chrome/browser/resources/print_preview/data/destination.html b/chromium/chrome/browser/resources/print_preview/data/destination.html new file mode 100644 index 00000000000..5e1d05995fc --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/data/destination.html @@ -0,0 +1,4 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="../print_preview_utils.html"> + +<script src="destination.js"></script> diff --git a/chromium/chrome/browser/resources/print_preview/data/destination.js b/chromium/chrome/browser/resources/print_preview/data/destination.js index e98026cea19..dd2db7704cf 100644 --- a/chromium/chrome/browser/resources/print_preview/data/destination.js +++ b/chromium/chrome/browser/resources/print_preview/data/destination.js @@ -56,39 +56,54 @@ print_preview.DestinationProvisionalType = { }; /** + * Capabilities of a print destination represented in a CDD. + * + * @typedef {{ + * vendor_capability: !Array<{Object}>, + * collate: ({default: (boolean|undefined)}|undefined), + * color: ({ + * option: !Array<{ + * type: (string|undefined), + * vendor_id: (string|undefined), + * custom_display_name: (string|undefined), + * is_default: (boolean|undefined) + * }> + * }|undefined), + * copies: ({default: (number|undefined), + * max: (number|undefined)}|undefined), + * duplex: ({option: !Array<{type: (string|undefined), + * is_default: (boolean|undefined)}>}|undefined), + * page_orientation: ({ + * option: !Array<{type: (string|undefined), + * is_default: (boolean|undefined)}> + * }|undefined), + * media_size: ({ + * option: !Array<{ + * type: (string|undefined), + * vendor_id: (string|undefined), + * custom_display_name: (string|undefined), + * is_default: (boolean|undefined) + * }> + * }|undefined), + * dpi: ({ + * option: !Array<{ + * vendor_id: (string|undefined), + * height_microns: number, + * width_microns: number, + * is_default: (boolean|undefined) + * }> + * }|undefined) + * }} + */ +print_preview.CddCapabilities; + +/** * The CDD (Cloud Device Description) describes the capabilities of a print * destination. * * @typedef {{ * version: string, - * printer: { - * vendor_capability: !Array<{Object}>, - * collate: ({default: (boolean|undefined)}|undefined), - * color: ({ - * option: !Array<{ - * type: (string|undefined), - * vendor_id: (string|undefined), - * custom_display_name: (string|undefined), - * is_default: (boolean|undefined) - * }> - * }|undefined), - * copies: ({default: (number|undefined), - * max: (number|undefined)}|undefined), - * duplex: ({option: !Array<{type: (string|undefined), - * is_default: (boolean|undefined)}>}|undefined), - * page_orientation: ({ - * option: !Array<{type: (string|undefined), - * is_default: (boolean|undefined)}> - * }|undefined), - * media_size: ({ - * option: !Array<{ - * type: (string|undefined), - * vendor_id: (string|undefined), - * custom_display_name: (string|undefined), - * is_default: (boolean|undefined) - * }> - * }|undefined) - * } + * printer: !print_preview.CddCapabilities, * }} */ print_preview.Cdd; @@ -430,8 +445,8 @@ cr.define('print_preview', function() { if (!this.isOffline) { return ''; } - var offlineDurationMs = Date.now() - this.lastAccessTime_; - var offlineMessageId; + const offlineDurationMs = Date.now() - this.lastAccessTime_; + let offlineMessageId; if (offlineDurationMs > 31622400000.0) { // One year. offlineMessageId = 'offlineForYear'; } else if (offlineDurationMs > 2678400000.0) { // One month. 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 165aa22f013..260a6411f31 100644 --- a/chromium/chrome/browser/resources/print_preview/data/destination_match.js +++ b/chromium/chrome/browser/resources/print_preview/data/destination_match.js @@ -11,7 +11,7 @@ cr.define('print_preview', function() { * return {?print_preview.PrinterType} The corresponding PrinterType. * Returns null if no match is found. */ - var originToType = function(origin) { + const originToType = function(origin) { if (origin === print_preview.DestinationOrigin.LOCAL || origin === print_preview.DestinationOrigin.CROS) { return print_preview.PrinterType.LOCAL_PRINTER; 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 80072b56887..cd83d8f608a 100644 --- a/chromium/chrome/browser/resources/print_preview/data/destination_store.js +++ b/chromium/chrome/browser/resources/print_preview/data/destination_store.js @@ -22,15 +22,15 @@ cr.define('print_preview', function() { * localize. * @return {!print_preview.Cdd} Localized capabilities. */ - var localizeCapabilities = function(capabilities) { + const localizeCapabilities = function(capabilities) { if (!capabilities.printer) return capabilities; - var mediaSize = capabilities.printer.media_size; + const mediaSize = capabilities.printer.media_size; if (!mediaSize) return capabilities; - for (var i = 0, media; (media = mediaSize.option[i]); i++) { + for (let i = 0, media; (media = mediaSize.option[i]); i++) { // No need to patch capabilities with localized names provided. if (!media.custom_display_name_localized) { media.custom_display_name = media.custom_display_name || @@ -46,9 +46,9 @@ cr.define('print_preview', function() { * @param {!Object} b Media to compare. * @return {number} 1 if a > b, -1 if a < b, or 0 if 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; + const compareMediaNames = function(a, b) { + const nameA = a.custom_display_name_localized || a.custom_display_name; + const nameB = b.custom_display_name_localized || b.custom_display_name; return nameA == nameB ? 0 : (nameA > nameB ? 1 : -1); }; @@ -59,11 +59,11 @@ cr.define('print_preview', function() { * @return {!print_preview.Cdd} Localized capabilities. * @private */ - var sortMediaSizes = function(capabilities) { + const sortMediaSizes = function(capabilities) { if (!capabilities.printer) return capabilities; - var mediaSize = capabilities.printer.media_size; + const mediaSize = capabilities.printer.media_size; if (!mediaSize) return capabilities; @@ -75,15 +75,15 @@ cr.define('print_preview', function() { // - Japanese // - Other metric // Otherwise, assume they are custom sizes. - var categoryStandardNA = []; - var categoryStandardCN = []; - var categoryStandardISO = []; - var categoryStandardJP = []; - var categoryStandardMisc = []; - var categoryCustom = []; - for (var i = 0, media; (media = mediaSize.option[i]); i++) { - var name = media.name || 'CUSTOM'; - var category; + const categoryStandardNA = []; + const categoryStandardCN = []; + const categoryStandardISO = []; + const categoryStandardJP = []; + const categoryStandardMisc = []; + const categoryCustom = []; + for (let i = 0, media; (media = mediaSize.option[i]); i++) { + const name = media.name || 'CUSTOM'; + let category; if (name.startsWith('NA_')) { category = categoryStandardNA; } else if ( @@ -288,6 +288,26 @@ cr.define('print_preview', function() { } /** + * @param {?string} filterAccount Account to filter recent destinations by. + * @return {!Array<!print_preview.Destination>} List of recent destinations + */ + getRecentDestinations(filterAccount) { + let recentDestinations = []; + this.appState_.recentDestinations.forEach(function(recentDestination) { + const origin = recentDestination.origin; + const id = recentDestination.id; + const account = recentDestination.account || ''; + const destination = + this.destinationMap_[this.getDestinationKey_(origin, id, account)]; + if (destination && + (!destination.account || destination.account == filterAccount)) { + recentDestinations.push(destination); + } + }.bind(this)); + return recentDestinations; + } + + /** * @return {print_preview.Destination} The currently selected destination or * {@code null} if none is selected. */ @@ -302,21 +322,21 @@ cr.define('print_preview', function() { } /** - * @return {boolean} Whether a search for local destinations is in progress. + * @return {boolean} Whether a search for print destinations is in progress. */ - get isLocalDestinationSearchInProgress() { - return Array.from(this.destinationSearchStatus_.values()) - .some( - el => el === - print_preview.DestinationStorePrinterSearchStatus.SEARCHING); - } + get isPrintDestinationSearchInProgress() { + let isLocalDestinationSearchInProgress = + Array.from(this.destinationSearchStatus_.values()) + .some( + el => el === + print_preview.DestinationStorePrinterSearchStatus + .SEARCHING); + if (isLocalDestinationSearchInProgress) + return true; - /** - * @return {boolean} Whether a search for cloud destinations is in progress. - */ - get isCloudDestinationSearchInProgress() { - return !!this.cloudPrintInterface_ && + let isCloudDestinationSearchInProgress = !!this.cloudPrintInterface_ && this.cloudPrintInterface_.isCloudDestinationSearchInProgress; + return isCloudDestinationSearchInProgress; } /** @@ -352,7 +372,7 @@ cr.define('print_preview', function() { this.createLocalPdfPrintDestination_(); if (!this.appState_.isSelectedDestinationValid()) { - var destinationMatch = this.convertToDestinationMatch_( + const destinationMatch = this.convertToDestinationMatch_( serializedDefaultDestinationSelectionRulesStr); if (destinationMatch) { this.fetchMatchingDestination_(destinationMatch); @@ -366,21 +386,21 @@ cr.define('print_preview', function() { return; } - var origin = null; - var id = ''; - var account = ''; - var name = ''; - var capabilities = null; - var extensionId = ''; - var extensionName = ''; - var foundDestination = false; + let origin = null; + let id = ''; + let account = ''; + let name = ''; + let capabilities = null; + let extensionId = ''; + let extensionName = ''; + let foundDestination = false; if (this.appState_.recentDestinations) { // Run through the destinations forward. As soon as we find a // destination, don't select any future destinations, just mark // them recent. Otherwise, there is a race condition between selecting // destinations/updating the print ticket and this selecting a new // destination that causes random print preview errors. - for (var i = 0; i < this.appState_.recentDestinations.length; i++) { + for (let i = 0; i < this.appState_.recentDestinations.length; i++) { origin = this.appState_.recentDestinations[i].origin; id = this.appState_.recentDestinations[i].id; account = this.appState_.recentDestinations[i].account || ''; @@ -389,7 +409,7 @@ cr.define('print_preview', function() { extensionId = this.appState_.recentDestinations[i].extensionId || ''; extensionName = this.appState_.recentDestinations[i].extensionName || ''; - var candidate = this.destinationMap_[this.getDestinationKey_( + const candidate = this.destinationMap_[this.getDestinationKey_( origin, id, account)]; if (candidate != null) { if (!foundDestination && !this.useSystemDefaultAsDefault_) @@ -413,10 +433,10 @@ cr.define('print_preview', function() { print_preview.DestinationOrigin.LOCAL : this.platformOrigin_; account = ''; - var candidate = + const systemDefaultCandidate = this.destinationMap_[this.getDestinationKey_(origin, id, account)]; - if (candidate != null) { - this.selectDestination(candidate); + if (systemDefaultCandidate != null) { + this.selectDestination(systemDefaultCandidate); return; } @@ -450,7 +470,7 @@ cr.define('print_preview', function() { this.autoSelectMatchingDestination_ = this.createExactDestinationMatch_(origin, id); - var type = print_preview.originToType(origin); + const type = print_preview.originToType(origin); if (type == print_preview.PrinterType.LOCAL_PRINTER) { this.nativeLayer_.getPrinterCapabilities(id, type).then( this.onCapabilitiesSet_.bind(this, origin, id), @@ -474,7 +494,7 @@ cr.define('print_preview', function() { // Create a fake selectedDestination_ that is not actually in the // destination store. When the real destination is created, this // destination will be overwritten. - var params = + const params = (origin === print_preview.DestinationOrigin.PRIVET) ? {} : { description: '', extensionId: extensionId, @@ -506,7 +526,7 @@ cr.define('print_preview', function() { */ fetchMatchingDestination_(destinationMatch) { this.autoSelectMatchingDestination_ = destinationMatch; - var type = destinationMatch.getType(); + const type = destinationMatch.getType(); if (type != null) { // Local, Privet, or Extension. this.startLoadDestinations(type); } else if ( @@ -526,7 +546,7 @@ cr.define('print_preview', function() { * @private */ convertToDestinationMatch_(serializedDefaultDestinationSelectionRulesStr) { - var matchRules = null; + let matchRules = null; try { if (serializedDefaultDestinationSelectionRulesStr) { matchRules = @@ -538,14 +558,14 @@ cr.define('print_preview', function() { if (!matchRules) return null; - var isLocal = !matchRules.kind || matchRules.kind == 'local'; - var isCloud = !matchRules.kind || matchRules.kind == 'cloud'; + const isLocal = !matchRules.kind || matchRules.kind == 'local'; + const isCloud = !matchRules.kind || matchRules.kind == 'cloud'; if (!isLocal && !isCloud) { console.error('Unsupported type: "' + matchRules.kind + '"'); return null; } - var origins = []; + const origins = []; if (isLocal) { origins.push(print_preview.DestinationOrigin.LOCAL); origins.push(print_preview.DestinationOrigin.PRIVET); @@ -557,7 +577,7 @@ cr.define('print_preview', function() { origins.push(print_preview.DestinationOrigin.DEVICE); } - var idRegExp = null; + let idRegExp = null; try { if (matchRules.idPattern) { idRegExp = new RegExp(matchRules.idPattern || '.*'); @@ -566,7 +586,7 @@ cr.define('print_preview', function() { console.error('Failed to parse regexp for "id": ' + e); } - var displayNameRegExp = null; + let displayNameRegExp = null; try { if (matchRules.namePattern) { displayNameRegExp = new RegExp(matchRules.namePattern || '.*'); @@ -689,7 +709,7 @@ cr.define('print_preview', function() { // Request destination capabilities from backend, since they are not // known yet. if (destination.capabilities == null) { - var type = print_preview.originToType(destination.origin); + const type = print_preview.originToType(destination.origin); if (type !== null) { this.nativeLayer_.getPrinterCapabilities(destination.id, type) .then( @@ -742,7 +762,7 @@ cr.define('print_preview', function() { * PROVISIONAL_DESTINATION_RESOLVED event. */ this.removeProvisionalDestination_(destination.id); - var parsedDestination = + const parsedDestination = print_preview.parseExtensionDestination(destinationInfo); this.insertIntoStore_(parsedDestination); this.dispatchProvisionalDestinationResolvedEvent_( @@ -765,7 +785,7 @@ cr.define('print_preview', function() { * @private */ selectPdfDestination_() { - var saveToPdfKey = this.getDestinationKey_( + const saveToPdfKey = this.getDestinationKey_( print_preview.DestinationOrigin.LOCAL, print_preview.Destination.GooglePromotedId.SAVE_AS_PDF, ''); this.selectDestination( @@ -823,7 +843,8 @@ cr.define('print_preview', function() { */ startLoadCloudDestinations(opt_origin) { if (this.cloudPrintInterface_ != null) { - var origins = this.loadedCloudOrigins_[this.userInfo_.activeUser] || []; + const origins = + this.loadedCloudOrigins_[this.userInfo_.activeUser] || []; if (origins.length == 0 || (opt_origin && origins.indexOf(opt_origin) < 0)) { this.cloudPrintInterface_.search( @@ -836,7 +857,7 @@ cr.define('print_preview', function() { /** Requests load of COOKIE based cloud destinations. */ reloadUserCookieBasedDestinations() { - var origins = this.loadedCloudOrigins_[this.userInfo_.activeUser] || []; + const origins = this.loadedCloudOrigins_[this.userInfo_.activeUser] || []; if (origins.indexOf(print_preview.DestinationOrigin.COOKIES) >= 0) { cr.dispatchSimpleEvent( this, DestinationStore.EventType.DESTINATION_SEARCH_DONE); @@ -849,7 +870,7 @@ cr.define('print_preview', function() { /** Initiates loading of all known destination types. */ startLoadAllDestinations() { this.startLoadCloudDestinations(); - for (var printerType of Object.values(print_preview.PrinterType)) { + for (const printerType of Object.values(print_preview.PrinterType)) { if (printerType !== print_preview.PrinterType.PDF_PRINTER) this.startLoadDestinations(printerType); } @@ -859,7 +880,7 @@ cr.define('print_preview', function() { * Wait for a privet device to be registered. */ waitForRegister(id) { - var privetType = print_preview.PrinterType.PRIVET_PRINTER; + const privetType = print_preview.PrinterType.PRIVET_PRINTER; this.nativeLayer_.getPrinters(privetType) .then(this.onDestinationSearchDone_.bind(this, privetType)); this.waitForRegisterDestination_ = id; @@ -891,7 +912,7 @@ cr.define('print_preview', function() { * destination if it was resolved successfully. */ dispatchProvisionalDestinationResolvedEvent_(provisionalId, destination) { - var event = new Event( + const event = new Event( DestinationStore.EventType.PROVISIONAL_DESTINATION_RESOLVED); event.provisionalId = provisionalId; event.destination = destination; @@ -920,7 +941,7 @@ cr.define('print_preview', function() { * @private */ insertDestinations_(destinations) { - var inserted = false; + let inserted = false; destinations.forEach(destination => { if (Array.isArray(destination)) { // privet printers return arrays of 1 or 2 printers @@ -949,7 +970,7 @@ cr.define('print_preview', function() { cr.dispatchSimpleEvent( this, DestinationStore.EventType.DESTINATIONS_INSERTED); if (this.autoSelectMatchingDestination_) { - var destinationsToSearch = + const destinationsToSearch = opt_destination && [opt_destination] || this.destinations_; destinationsToSearch.some(function(destination) { if (this.autoSelectMatchingDestination_.match(destination)) { @@ -974,7 +995,8 @@ cr.define('print_preview', function() { print_preview.PrinterType.LOCAL_PRINTER) { destination.capabilities_ = sortMediaSizes(destination.capabilities_); } - var existingDestination = this.destinationMap_[this.getKey_(destination)]; + const existingDestination = + this.destinationMap_[this.getKey_(destination)]; if (existingDestination != null) { existingDestination.capabilities = destination.capabilities; } else { @@ -1015,8 +1037,8 @@ cr.define('print_preview', function() { * @private */ insertIntoStore_(destination) { - var key = this.getKey_(destination); - var existingDestination = this.destinationMap_[key]; + const key = this.getKey_(destination); + const existingDestination = this.destinationMap_[key]; if (existingDestination == null) { destination.isRecent |= this.appState_.recentDestinations.some(function(recent) { @@ -1064,7 +1086,7 @@ cr.define('print_preview', function() { this.destinationMap_ = {}; this.selectDestination(null); this.loadedCloudOrigins_ = {}; - for (var printerType of Object.values(print_preview.PrinterType)) { + for (const printerType of Object.values(print_preview.PrinterType)) { if (printerType !== print_preview.PrinterType.PDF_PRINTER) { this.destinationSearchStatus_.set( printerType, @@ -1107,9 +1129,9 @@ cr.define('print_preview', function() { * @private */ onCapabilitiesSet_(origin, id, settingsInfo) { - var dest = null; + let dest = null; if (origin !== print_preview.DestinationOrigin.PRIVET) { - var key = this.getDestinationKey_(origin, id, ''); + const key = this.getDestinationKey_(origin, id, ''); dest = this.destinationMap_[key]; } if (!dest) { @@ -1122,7 +1144,15 @@ cr.define('print_preview', function() { print_preview.originToType(origin), assert(settingsInfo.printer)); } if (dest) { - var updateDestination = destination => { + if ((origin === print_preview.DestinationOrigin.LOCAL || + origin === print_preview.DestinationOrigin.CROS) && + dest.capabilities) { + // If capabilities are already set for this destination ignore new + // results. This prevents custom margins from being cleared as long + // as the user does not change to a new non-recent destination. + return; + } + const updateDestination = destination => { destination.capabilities = settingsInfo.capabilities; this.updateDestination_(destination); }; @@ -1148,7 +1178,7 @@ cr.define('print_preview', function() { 'Failed to get print capabilities for printer ' + destinationId); if (this.selectedDestination_ && this.selectedDestination_.id == destinationId) { - var event = + const event = new Event(DestinationStore.EventType.SELECTED_DESTINATION_INVALID); event.destinationId = destinationId; this.dispatchEvent(event); @@ -1171,7 +1201,7 @@ cr.define('print_preview', function() { this.insertDestinations_(event.printers); } if (event.searchDone) { - var origins = this.loadedCloudOrigins_[event.user] || []; + const origins = this.loadedCloudOrigins_[event.user] || []; if (origins.indexOf(event.origin) < 0) { this.loadedCloudOrigins_[event.user] = origins.concat([event.origin]); } @@ -1234,7 +1264,7 @@ cr.define('print_preview', function() { */ onPrintersAdded_(type, printers) { if (type == print_preview.PrinterType.PRIVET_PRINTER) { - var printer = + const printer = /** !print_preview.PrivetPrinterDescription */ (printers[0]); if (printer.serviceName == this.waitForRegisterDestination_ && !printer.isUnregistered) { diff --git a/chromium/chrome/browser/resources/print_preview/data/document_info.html b/chromium/chrome/browser/resources/print_preview/data/document_info.html new file mode 100644 index 00000000000..d814e19a661 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/data/document_info.html @@ -0,0 +1,7 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/cr/event_target.html"> +<link rel="import" href="size.html"> +<link rel="import" href="coordinate2d.html"> +<link rel="import" href="printable_area.html"> + +<script src="document_info.js"></script> diff --git a/chromium/chrome/browser/resources/print_preview/data/document_info.js b/chromium/chrome/browser/resources/print_preview/data/document_info.js index 2e286f35ead..8881596f6f3 100644 --- a/chromium/chrome/browser/resources/print_preview/data/document_info.js +++ b/chromium/chrome/browser/resources/print_preview/data/document_info.js @@ -5,110 +5,93 @@ cr.define('print_preview', function() { 'use strict'; - /** - * Data model which contains information related to the document to print. - * @constructor - * @extends {cr.EventTarget} - */ - function DocumentInfo() { - cr.EventTarget.call(this); - - /** - * Whether the document is styled by CSS media styles. - * @type {boolean} - * @private - */ - this.hasCssMediaStyles_ = false; - - /** - * Whether the document has selected content. - * @type {boolean} - * @private - */ - this.hasSelection_ = false; - - /** - * Whether the document to print is modifiable (i.e. can be reflowed). - * @type {boolean} - * @private - */ - this.isModifiable_ = true; - - /** - * Whether scaling of the document is prohibited. - * @type {boolean} - * @private - */ - this.isScalingDisabled_ = false; - - /** - * Margins of the document in points. - * @type {print_preview.Margins} - * @private - */ - this.margins_ = null; - - /** - * Number of pages in the document to print. - * @type {number} - * @private - */ - this.pageCount_ = 0; - - // Create the document info with some initial settings. Actual - // page-related information won't be set until preview generation occurs, - // so we'll use some defaults until then. This way, the print ticket store - // will be valid even if no preview can be generated. - var initialPageSize = new print_preview.Size(612, 792); // 8.5"x11" - - /** - * Size of the pages of the document in points. - * @type {!print_preview.Size} - * @private - */ - this.pageSize_ = initialPageSize; - - /** - * Printable area of the document in points. - * @type {!print_preview.PrintableArea} - * @private - */ - this.printableArea_ = new print_preview.PrintableArea( - new print_preview.Coordinate2d(0, 0), initialPageSize); - - /** - * Title of document. - * @type {string} - * @private - */ - this.title_ = ''; - - /** - * Whether this data model has been initialized. - * @type {boolean} - * @private - */ - this.isInitialized_ = false; - } - - /** - * Event types dispatched by this data model. - * @enum {string} - */ - DocumentInfo.EventType = {CHANGE: 'print_preview.DocumentInfo.CHANGE'}; - - DocumentInfo.prototype = { - __proto__: cr.EventTarget.prototype, + class DocumentInfo extends cr.EventTarget { + /** + * Data model which contains information related to the document to print. + */ + constructor() { + super(); + + /** + * Whether the document is styled by CSS media styles. + * @private {boolean} + */ + this.hasCssMediaStyles_ = false; + + /** + * Whether the document has selected content. + * @private {boolean} + */ + this.hasSelection_ = false; + + /** + * Whether the document to print is modifiable (i.e. can be reflowed). + * @private {boolean} + */ + this.isModifiable_ = true; + + /** + * Whether scaling of the document is prohibited. + * @private {boolean} + */ + this.isScalingDisabled_ = false; + + /** + * Scaling required to fit to page. + * @private {number} + */ + this.fitToPageScaling_ = 100; + + /** + * Margins of the document in points. + * @private {print_preview.Margins} + */ + this.margins_ = null; + + /** + * Number of pages in the document to print. + * @private {number} + */ + this.pageCount_ = 0; + + /** + * Size of the pages of the document in points. Actual page-related + * information won't be set until preview generation occurs, so use + * a default value until then. This way, the print ticket store will be + * valid even if no preview can be generated. + * @private {!print_preview.Size} + */ + this.pageSize_ = new print_preview.Size(612, 792); // 8.5"x11" + + /** + * Printable area of the document in points. + * @private {!print_preview.PrintableArea} + */ + this.printableArea_ = new print_preview.PrintableArea( + new print_preview.Coordinate2d(0, 0), this.pageSize_); + + /** + * Title of document. + * @private {string} + */ + this.title_ = ''; + + /** + * Whether this data model has been initialized. + * @private {boolean} + */ + this.isInitialized_ = false; + } /** @return {boolean} Whether the document is styled by CSS media styles. */ get hasCssMediaStyles() { return this.hasCssMediaStyles_; - }, + } /** @return {boolean} Whether the document has selected content. */ get hasSelection() { return this.hasSelection_; - }, + } /** * @return {boolean} Whether the document to print is modifiable (i.e. can @@ -116,22 +99,27 @@ cr.define('print_preview', function() { */ get isModifiable() { return this.isModifiable_; - }, + } /** @return {boolean} Whether scaling of the document is prohibited. */ get isScalingDisabled() { return this.isScalingDisabled_; - }, + } + + /** @return {number} Scaling required to fit to page. */ + get fitToPageScaling() { + return this.fitToPageScaling_; + } /** @return {print_preview.Margins} Margins of the document in points. */ get margins() { return this.margins_; - }, + } /** @return {number} Number of pages in the document to print. */ get pageCount() { return this.pageCount_; - }, + } /** * @return {!print_preview.Size} Size of the pages of the document in @@ -139,7 +127,7 @@ cr.define('print_preview', function() { */ get pageSize() { return this.pageSize_; - }, + } /** * @return {!print_preview.PrintableArea} Printable area of the document in @@ -147,12 +135,12 @@ cr.define('print_preview', function() { */ get printableArea() { return this.printableArea_; - }, + } /** @return {string} Title of document. */ get title() { return this.title_; - }, + } /** * Initializes the state of the data model and dispatches a CHANGE event. @@ -161,13 +149,13 @@ cr.define('print_preview', function() { * @param {boolean} hasSelection Whether the document has user-selected * content. */ - init: function(isModifiable, title, hasSelection) { + init(isModifiable, title, hasSelection) { this.isModifiable_ = isModifiable; this.title_ = title; this.hasSelection_ = hasSelection; this.isInitialized_ = true; cr.dispatchSimpleEvent(this, DocumentInfo.EventType.CHANGE); - }, + } /** * Updates whether scaling is disabled for the document and dispatches a @@ -175,24 +163,24 @@ cr.define('print_preview', function() { * @param {boolean} isScalingDisabled Whether scaling of the document is * prohibited. */ - updateIsScalingDisabled: function(isScalingDisabled) { + updateIsScalingDisabled(isScalingDisabled) { if (this.isInitialized_ && this.isScalingDisabled_ != isScalingDisabled) { this.isScalingDisabled_ = isScalingDisabled; cr.dispatchSimpleEvent(this, DocumentInfo.EventType.CHANGE); } - }, + } /** * Updates the total number of pages in the document and dispatches a CHANGE * event. * @param {number} pageCount Number of pages in the document. */ - updatePageCount: function(pageCount) { + updatePageCount(pageCount) { if (this.isInitialized_ && this.pageCount_ != pageCount) { this.pageCount_ = pageCount; cr.dispatchSimpleEvent(this, DocumentInfo.EventType.CHANGE); } - }, + } /** * Updates information about each page and dispatches a CHANGE event. @@ -204,8 +192,7 @@ cr.define('print_preview', function() { * media styles. * @param {print_preview.Margins} margins Margins of the document in points. */ - updatePageInfo: function( - printableArea, pageSize, hasCssMediaStyles, margins) { + updatePageInfo(printableArea, pageSize, hasCssMediaStyles, margins) { if (this.isInitialized_ && (!this.printableArea_.equals(printableArea) || !this.pageSize_.equals(pageSize) || @@ -218,7 +205,13 @@ cr.define('print_preview', function() { cr.dispatchSimpleEvent(this, DocumentInfo.EventType.CHANGE); } } - }; + } + + /** + * Event types dispatched by this data model. + * @enum {string} + */ + DocumentInfo.EventType = {CHANGE: 'print_preview.DocumentInfo.CHANGE'}; // Export return {DocumentInfo: DocumentInfo}; diff --git a/chromium/chrome/browser/resources/print_preview/data/invitation_store.js b/chromium/chrome/browser/resources/print_preview/data/invitation_store.js index 41f654fac14..72ed0849a0d 100644 --- a/chromium/chrome/browser/resources/print_preview/data/invitation_store.js +++ b/chromium/chrome/browser/resources/print_preview/data/invitation_store.js @@ -17,66 +17,52 @@ print_preview.InvitationStoreLoadStatus = { cr.define('print_preview', function() { 'use strict'; - /** - * Printer sharing invitations data store. - * @param {!print_preview.UserInfo} userInfo User information repository. - * @constructor - * @extends {cr.EventTarget} - */ - function InvitationStore(userInfo) { - cr.EventTarget.call(this); - + class InvitationStore extends cr.EventTarget { /** - * User information repository. - * @private {!print_preview.UserInfo} + * Printer sharing invitations data store. + * @param {!print_preview.UserInfo} userInfo User information repository. */ - this.userInfo_ = userInfo; - - /** - * Maps user account to the list of invitations for this account. - * @private {!Object<!Array<!print_preview.Invitation>>} - */ - this.invitations_ = {}; - - /** - * Maps user account to the flag whether the invitations for this account - * were successfully loaded. - * @private {!Object<print_preview.InvitationStoreLoadStatus>} - */ - this.loadStatus_ = {}; - - /** - * Event tracker used to track event listeners of the destination store. - * @private {!EventTracker} - */ - this.tracker_ = new EventTracker(); - - /** - * Used to fetch and process invitations. - * @private {cloudprint.CloudPrintInterface} - */ - this.cloudPrintInterface_ = null; - - /** - * Invitation being processed now. Only one invitation can be processed at - * a time. - * @private {print_preview.Invitation} - */ - this.invitationInProgress_ = null; - } - - /** - * Event types dispatched by the data store. - * @enum {string} - */ - InvitationStore.EventType = { - INVITATION_PROCESSED: 'print_preview.InvitationStore.INVITATION_PROCESSED', - INVITATION_SEARCH_DONE: - 'print_preview.InvitationStore.INVITATION_SEARCH_DONE' - }; - - InvitationStore.prototype = { - __proto__: cr.EventTarget.prototype, + constructor(userInfo) { + super(); + + /** + * User information repository. + * @private {!print_preview.UserInfo} + */ + this.userInfo_ = userInfo; + + /** + * Maps user account to the list of invitations for this account. + * @private {!Object<!Array<!print_preview.Invitation>>} + */ + this.invitations_ = {}; + + /** + * Maps user account to the flag whether the invitations for this account + * were successfully loaded. + * @private {!Object<print_preview.InvitationStoreLoadStatus>} + */ + this.loadStatus_ = {}; + + /** + * Event tracker used to track event listeners of the destination store. + * @private {!EventTracker} + */ + this.tracker_ = new EventTracker(); + + /** + * Used to fetch and process invitations. + * @private {cloudprint.CloudPrintInterface} + */ + this.cloudPrintInterface_ = null; + + /** + * Invitation being processed now. Only one invitation can be processed at + * a time. + * @private {print_preview.Invitation} + */ + this.invitationInProgress_ = null; + } /** * @return {print_preview.Invitation} Currently processed invitation or @@ -84,23 +70,23 @@ cr.define('print_preview', function() { */ get invitationInProgress() { return this.invitationInProgress_; - }, + } /** * @param {string} account Account to filter invitations by. * @return {!Array<!print_preview.Invitation>} List of invitations for the * {@code account}. */ - invitations: function(account) { + invitations(account) { return this.invitations_[account] || []; - }, + } /** * Sets the invitation 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( @@ -119,10 +105,10 @@ cr.define('print_preview', function() { this.cloudPrintInterface_, cloudprint.CloudPrintInterfaceEventType.PROCESS_INVITE_FAILED, this.onCloudPrintProcessInviteFailed_.bind(this)); - }, + } /** Initiates loading of cloud printer sharing invitations. */ - startLoadingInvitations: function() { + startLoadingInvitations() { if (!this.cloudPrintInterface_) return; if (!this.userInfo_.activeUser) @@ -139,26 +125,26 @@ cr.define('print_preview', function() { this.loadStatus_[this.userInfo_.activeUser] = print_preview.InvitationStoreLoadStatus.IN_PROGRESS; this.cloudPrintInterface_.invites(this.userInfo_.activeUser); - }, + } /** * Accepts or rejects the {@code invitation}, based on {@code accept} value. * @param {!print_preview.Invitation} invitation Invitation to process. * @param {boolean} accept Whether to accept this invitation. */ - processInvitation: function(invitation, accept) { + processInvitation(invitation, accept) { if (this.invitationInProgress_) return; this.invitationInProgress_ = invitation; this.cloudPrintInterface_.processInvite(invitation, accept); - }, + } /** * Removes processed invitation from the internal storage. * @param {!print_preview.Invitation} invitation Processed invitation. * @private */ - invitationProcessed_: function(invitation) { + invitationProcessed_(invitation) { if (this.invitations_.hasOwnProperty(invitation.account)) { this.invitations_[invitation.account] = this.invitations_[invitation.account].filter(function(i) { @@ -167,31 +153,31 @@ cr.define('print_preview', function() { } if (this.invitationInProgress_ == invitation) this.invitationInProgress_ = null; - }, + } /** * Called when printer sharing invitations are fetched. * @param {Event} event Contains the list of invitations. * @private */ - onCloudPrintInvitesDone_: function(event) { + onCloudPrintInvitesDone_(event) { this.loadStatus_[event.user] = print_preview.InvitationStoreLoadStatus.DONE; this.invitations_[event.user] = event.invitations; cr.dispatchSimpleEvent( this, InvitationStore.EventType.INVITATION_SEARCH_DONE); - }, + } /** * Called when printer sharing invitations fetch has failed. * @param {Event} event Contains the reason of failure. * @private */ - onCloudPrintInvitesFailed_: function(event) { + onCloudPrintInvitesFailed_(event) { this.loadStatus_[event.user] = print_preview.InvitationStoreLoadStatus.FAILED; - }, + } /** * Called when printer sharing invitation was processed successfully. @@ -199,11 +185,11 @@ cr.define('print_preview', function() { * newly accepted destination. * @private */ - onCloudPrintProcessInviteDone_: function(event) { + onCloudPrintProcessInviteDone_(event) { this.invitationProcessed_(event.invitation); cr.dispatchSimpleEvent( this, InvitationStore.EventType.INVITATION_PROCESSED); - }, + } /** * Called when /printer call completes. Updates the specified destination's @@ -212,12 +198,22 @@ cr.define('print_preview', function() { * destination. * @private */ - onCloudPrintProcessInviteFailed_: function(event) { + onCloudPrintProcessInviteFailed_(event) { this.invitationProcessed_(event.invitation); // TODO: Display an error. cr.dispatchSimpleEvent( this, InvitationStore.EventType.INVITATION_PROCESSED); } + } + + /** + * Event types dispatched by the data store. + * @enum {string} + */ + InvitationStore.EventType = { + INVITATION_PROCESSED: 'print_preview.InvitationStore.INVITATION_PROCESSED', + INVITATION_SEARCH_DONE: + 'print_preview.InvitationStore.INVITATION_SEARCH_DONE' }; // Export 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 9b09d62e06f..e2f1597c963 100644 --- a/chromium/chrome/browser/resources/print_preview/data/local_parsers.js +++ b/chromium/chrome/browser/resources/print_preview/data/local_parsers.js @@ -16,7 +16,7 @@ cr.define('print_preview', function() { * For EXTENSION_PRINTER => print_preview.ProvisionalDestinationInfo * @return {!Array<!print_preview.Destination> | !print_preview.Destination} */ - var parseDestination = function(type, printer) { + function parseDestination(type, printer) { if (type === print_preview.PrinterType.LOCAL_PRINTER) { return parseLocalDestination( /** @type {!print_preview.LocalDestinationInfo} */ (printer)); @@ -31,7 +31,7 @@ cr.define('print_preview', function() { } assertNotReached('Unknown printer type ' + type); return []; - }; + } /** * Parses a local print destination. @@ -39,8 +39,8 @@ cr.define('print_preview', function() { * describing a local print destination. * @return {!print_preview.Destination} Parsed local print destination. */ - var parseLocalDestination = function(destinationInfo) { - var options = { + function parseLocalDestination(destinationInfo) { + const options = { description: destinationInfo.printerDescription, isEnterprisePrinter: destinationInfo.cupsEnterprisePrinter }; @@ -57,7 +57,7 @@ cr.define('print_preview', function() { print_preview.DestinationOrigin.LOCAL, destinationInfo.printerName, false /*isRecent*/, print_preview.DestinationConnectionStatus.ONLINE, options); - }; + } /** * Parses a privet destination as one or more local printers. @@ -66,8 +66,8 @@ cr.define('print_preview', function() { * @return {!print_preview.Destination | * !Array<!print_preview.Destination>} Parsed destination info. */ - var parsePrivetDestination = function(destinationInfo) { - var returnedPrinters = []; + function parsePrivetDestination(destinationInfo) { + const returnedPrinters = []; if (destinationInfo.hasLocalPrinting) { returnedPrinters.push(new print_preview.Destination( @@ -87,7 +87,7 @@ cr.define('print_preview', function() { return returnedPrinters.length === 1 ? returnedPrinters[0] : returnedPrinters; - }; + } /** * Parses an extension destination from an extension supplied printer @@ -96,8 +96,8 @@ cr.define('print_preview', function() { * describing an extension printer. * @return {!print_preview.Destination} Parsed destination. */ - var parseExtensionDestination = function(destinationInfo) { - var provisionalType = destinationInfo.provisional ? + function parseExtensionDestination(destinationInfo) { + const provisionalType = destinationInfo.provisional ? print_preview.DestinationProvisionalType.NEEDS_USB_PERMISSION : print_preview.DestinationProvisionalType.NONE; @@ -111,7 +111,7 @@ cr.define('print_preview', function() { extensionName: destinationInfo.extensionName || '', provisionalType: provisionalType }); - }; + } // Export return { diff --git a/chromium/chrome/browser/resources/print_preview/data/margins.js b/chromium/chrome/browser/resources/print_preview/data/margins.js index b7bef50c686..23088d9770d 100644 --- a/chromium/chrome/browser/resources/print_preview/data/margins.js +++ b/chromium/chrome/browser/resources/print_preview/data/margins.js @@ -2,6 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +cr.exportPath('print_preview.ticket_items'); +/** + * Enumeration of the orientations of margins. + * @enum {string} + */ +print_preview.ticket_items.CustomMarginsOrientation = { + TOP: 'top', + RIGHT: 'right', + BOTTOM: 'bottom', + LEFT: 'left' +}; + cr.define('print_preview', function() { 'use strict'; @@ -63,7 +75,7 @@ cr.define('print_preview', function() { * modification made to the specified margin. */ set(orientation, value) { - var newValue = this.clone_(); + const newValue = this.clone_(); newValue[orientation] = value; return new Margins( newValue[print_preview.ticket_items.CustomMarginsOrientation.TOP], @@ -81,7 +93,7 @@ cr.define('print_preview', function() { if (other == null) { return false; } - for (var orientation in this.value_) { + for (const orientation in this.value_) { if (this.value_[orientation] != other.value_[orientation]) { return false; } @@ -99,8 +111,8 @@ cr.define('print_preview', function() { * @private */ clone_() { - var clone = {}; - for (var o in this.value_) { + const clone = {}; + for (const o in this.value_) { clone[o] = this.value_[o]; } return clone; 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 1548de6b4e1..eaaa45c6ae2 100644 --- a/chromium/chrome/browser/resources/print_preview/data/measurement_system.js +++ b/chromium/chrome/browser/resources/print_preview/data/measurement_system.js @@ -13,73 +13,52 @@ print_preview.MeasurementSystemUnitType = { IMPERIAL: 1 // inches }; +/** + * @typedef {{precision: number, + * decimalPlaces: number, + * ptsPerUnit: number, + * unitSymbol: string}} + */ +print_preview.MeasurementSystemPrefs; + cr.define('print_preview', function() { 'use strict'; - /** - * Measurement system of the print preview. Used to parse and serialize point - * measurements into the system's local units (e.g. millimeters, inches). - * @param {string} thousandsDelimeter Delimeter between thousands digits. - * @param {string} decimalDelimeter Delimeter between integers and decimals. - * @param {!print_preview.MeasurementSystemUnitType} unitType Measurement - * unit type of the system. - * @constructor - */ - function MeasurementSystem(thousandsDelimeter, decimalDelimeter, unitType) { - this.thousandsDelimeter_ = thousandsDelimeter || ','; - this.decimalDelimeter_ = decimalDelimeter || '.'; - this.unitType_ = unitType; - } - - /** - * Maximum resolution of local unit values. - * @type {!Object<!print_preview.MeasurementSystemUnitType, number>} - * @private - */ - MeasurementSystem.Precision_ = {}; - MeasurementSystem.Precision_[print_preview.MeasurementSystemUnitType.METRIC] = - 0.5; - MeasurementSystem - .Precision_[print_preview.MeasurementSystemUnitType.IMPERIAL] = 0.01; - - /** - * Maximum number of decimal places to keep for local unit. - * @type {!Object<!print_preview.MeasurementSystemUnitType, number>} - * @private - */ - MeasurementSystem.DecimalPlaces_ = {}; - MeasurementSystem - .DecimalPlaces_[print_preview.MeasurementSystemUnitType.METRIC] = 1; - MeasurementSystem - .DecimalPlaces_[print_preview.MeasurementSystemUnitType.IMPERIAL] = 2; - - /** - * Number of points per inch. - * @type {number} - * @const - * @private - */ - MeasurementSystem.PTS_PER_INCH_ = 72.0; - - /** - * Number of points per millimeter. - * @type {number} - * @const - * @private - */ - MeasurementSystem.PTS_PER_MM_ = MeasurementSystem.PTS_PER_INCH_ / 25.4; + class MeasurementSystem { + /** + * Measurement system of the print preview. Used to parse and serialize + * point measurements into the system's local units (e.g. millimeters, + * inches). + * @param {string} thousandsDelimeter Delimeter between thousands digits. + * @param {string} decimalDelimeter Delimeter between integers and decimals. + * @param {!print_preview.MeasurementSystemUnitType} unitType Measurement + * unit type of the system. + */ + constructor(thousandsDelimeter, decimalDelimeter, unitType) { + /** + * The thousands delimeter to use when displaying numbers. + * @private {string} + */ + this.thousandsDelimeter_ = thousandsDelimeter || ','; + + /** + * The decimal delimeter to use when displaying numbers. + * @private {string} + */ + this.decimalDelimeter_ = decimalDelimeter || '.'; + + assert(measurementSystemPrefs.has(unitType)); + /** + * The measurement system preferences based on the unit type. + * @private {!print_preview.MeasurementSystemPrefs} + */ + this.measurementSystemPrefs_ = measurementSystemPrefs.get(unitType); + } - MeasurementSystem.prototype = { /** @return {string} The unit type symbol of the measurement system. */ get unitSymbol() { - if (this.unitType_ == print_preview.MeasurementSystemUnitType.METRIC) { - return 'mm'; - } - if (this.unitType_ == print_preview.MeasurementSystemUnitType.IMPERIAL) { - return '"'; - } - throw Error('Unit type not supported: ' + this.unitType_); - }, + return this.measurementSystemPrefs_.unitSymbol; + } /** * @return {string} The thousands delimeter character of the measurement @@ -87,7 +66,7 @@ cr.define('print_preview', function() { */ get thousandsDelimeter() { return this.thousandsDelimeter_; - }, + } /** * @return {string} The decimal delimeter character of the measurement @@ -95,49 +74,70 @@ cr.define('print_preview', function() { */ get decimalDelimeter() { return this.decimalDelimeter_; - }, + } - setSystem: function(thousandsDelimeter, decimalDelimeter, unitType) { + /** + * Sets the measurement system based on the delimeters and unit type. + * @param {string} thousandsDelimeter The thousands delimeter to use + * @param {string} decimalDelimeter The decimal delimeter to use + * @param {!print_preview.MeasurementSystemUnitType} unitType Measurement + * unit type of the system. + */ + setSystem(thousandsDelimeter, decimalDelimeter, unitType) { this.thousandsDelimeter_ = thousandsDelimeter; this.decimalDelimeter_ = decimalDelimeter; - this.unitType_ = unitType; - }, + assert(measurementSystemPrefs.has(unitType)); + this.measurementSystemPrefs_ = measurementSystemPrefs.get(unitType); + } /** * Rounds a value in the local system's units to the appropriate precision. * @param {number} value Value to round. * @return {number} Rounded value. */ - roundValue: function(value) { - var precision = MeasurementSystem.Precision_[this.unitType_]; - var roundedValue = Math.round(value / precision) * precision; + roundValue(value) { + const precision = this.measurementSystemPrefs_.precision; + const roundedValue = Math.round(value / precision) * precision; // Truncate - return +roundedValue.toFixed( - MeasurementSystem.DecimalPlaces_[this.unitType_]); - }, + return +roundedValue.toFixed(this.measurementSystemPrefs_.decimalPlaces); + } /** * @param {number} pts Value in points to convert to local units. * @return {number} Value in local units. */ - convertFromPoints: function(pts) { - if (this.unitType_ == print_preview.MeasurementSystemUnitType.METRIC) { - return pts / MeasurementSystem.PTS_PER_MM_; - } - return pts / MeasurementSystem.PTS_PER_INCH_; - }, + convertFromPoints(pts) { + return pts / this.measurementSystemPrefs_.ptsPerUnit; + } /** * @param {number} localUnits Value in local units to convert to points. * @return {number} Value in points. */ - convertToPoints: function(localUnits) { - if (this.unitType_ == print_preview.MeasurementSystemUnitType.METRIC) { - return localUnits * MeasurementSystem.PTS_PER_MM_; - } - return localUnits * MeasurementSystem.PTS_PER_INCH_; + convertToPoints(localUnits) { + return localUnits * this.measurementSystemPrefs_.ptsPerUnit; } - }; + } + + /** + * Maximum resolution and number of decimal places for local unit values. + * @private {!Map<!print_preview.MeasurementSystemUnitType, + * !print_preview.MeasurementSystemPrefs>} + */ + const measurementSystemPrefs = new Map([ + [ + print_preview.MeasurementSystemUnitType.METRIC, { + precision: 0.5, + decimalPlaces: 1, + ptsPerUnit: 72.0 / 25.4, + unitSymbol: 'mm' + } + ], + [ + print_preview.MeasurementSystemUnitType.IMPERIAL, + {precision: 0.01, decimalPlaces: 2, ptsPerUnit: 72.0, unitSymbol: '"'} + ] + ]); // Export return {MeasurementSystem: MeasurementSystem}; 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 6675843b53e..9a643f1263a 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 @@ -47,6 +47,12 @@ cr.define('print_preview', function() { this.documentInfo_ = documentInfo; /** + * The destination that capabilities were last received for. + * @private {?print_preview.Destination} + */ + this.destination_ = null; + + /** * Printing capabilities of Chromium and the currently selected * destination. * @type {!print_preview.CapabilitiesHolder} @@ -421,12 +427,12 @@ cr.define('print_preview', function() { destination.capabilities, 'Trying to create a Google Cloud Print print ticket for a ' + 'destination with no print capabilities'); - var cjt = {version: '1.0', print: {}}; + const cjt = {version: '1.0', print: {}}; if (this.collate.isCapabilityAvailable() && this.collate.isUserEdited()) { cjt.print.collate = {collate: this.collate.getValue()}; } if (this.color.isCapabilityAvailable() && this.color.isUserEdited()) { - var selectedOption = this.color.getSelectedOption(); + const selectedOption = this.color.getSelectedOption(); if (!selectedOption) { console.error('Could not find correct color option'); } else { @@ -445,12 +451,12 @@ cr.define('print_preview', function() { }; } if (this.mediaSize.isCapabilityAvailable()) { - var value = this.mediaSize.getValue(); + const mediaValue = this.mediaSize.getValue(); cjt.print.media_size = { - width_microns: value.width_microns, - height_microns: value.height_microns, - is_continuous_feed: value.is_continuous_feed, - vendor_id: value.vendor_id + width_microns: mediaValue.width_microns, + height_microns: mediaValue.height_microns, + is_continuous_feed: mediaValue.is_continuous_feed, + vendor_id: mediaValue.vendor_id }; } if (!this.landscape.isCapabilityAvailable()) { @@ -465,18 +471,18 @@ cr.define('print_preview', function() { }; } if (this.dpi.isCapabilityAvailable()) { - var value = this.dpi.getValue(); + const dpiValue = this.dpi.getValue(); cjt.print.dpi = { - horizontal_dpi: value.horizontal_dpi, - vertical_dpi: value.vertical_dpi, - vendor_id: value.vendor_id + horizontal_dpi: dpiValue.horizontal_dpi, + vertical_dpi: dpiValue.vertical_dpi, + vendor_id: dpiValue.vendor_id }; } if (this.vendorItems.isCapabilityAvailable() && this.vendorItems.isUserEdited()) { - var items = this.vendorItems.ticketItems; + const items = this.vendorItems.ticketItems; cjt.print.vendor_ticket_item = []; - for (var itemId in items) { + for (const itemId in items) { if (items.hasOwnProperty(itemId)) { cjt.print.vendor_ticket_item.push( {id: itemId, value: items[itemId]}); @@ -517,7 +523,11 @@ cr.define('print_preview', function() { * @private */ onSelectedDestinationCapabilitiesReady_() { - if (this.capabilitiesHolder_.get() != null) { + const selectedDestination = this.destinationStore_.selectedDestination; + const isFirstUpdate = this.capabilitiesHolder_.get() == null; + // Only clear the ticket items if the user selected a new destination + // and this is not the first update. + if (!isFirstUpdate && this.destination_ != selectedDestination) { this.customMargins_.updateValue(null); if (this.marginsType_.getValue() == print_preview.ticket_items.MarginsTypeValue.CUSTOM) { @@ -526,10 +536,9 @@ cr.define('print_preview', function() { } this.vendorItems_.updateValue({}); } - var caps = - assert(this.destinationStore_.selectedDestination.capabilities); - var isFirstUpdate = this.capabilitiesHolder_.get() == null; + const caps = assert(selectedDestination.capabilities); this.capabilitiesHolder_.set(caps); + this.destination_ = selectedDestination; if (isFirstUpdate) { this.isInitialized_ = true; cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.INITIALIZE); diff --git a/chromium/chrome/browser/resources/print_preview/data/printable_area.html b/chromium/chrome/browser/resources/print_preview/data/printable_area.html new file mode 100644 index 00000000000..d6801fda881 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/data/printable_area.html @@ -0,0 +1,5 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="size.html"> +<link rel="import" href="coordinate2d.html"> + +<script src="printable_area.js"></script> diff --git a/chromium/chrome/browser/resources/print_preview/data/size.html b/chromium/chrome/browser/resources/print_preview/data/size.html new file mode 100644 index 00000000000..8eeefa075d2 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/data/size.html @@ -0,0 +1,3 @@ +<link rel="import" href="chrome://resources/html/cr.html"> + +<script src="size.js"></script> 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 227cf03cf2d..9deaf2a895d 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 @@ -32,7 +32,7 @@ cr.define('print_preview.ticket_items', function() { /** @override */ getDefaultValueInternal() { - var capability = this.getCollateCapability_(); + const capability = this.getCollateCapability_(); return capability.hasOwnProperty('default') ? capability.default : true; } @@ -46,7 +46,7 @@ cr.define('print_preview.ticket_items', function() { * @private */ getCollateCapability_() { - var dest = this.getSelectedDestInternal(); + const dest = this.getSelectedDestInternal(); return (dest && dest.capabilities && dest.capabilities.printer && dest.capabilities.printer.collate) || null; 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 a78d6ccc9e1..2df29ad3ba2 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 @@ -27,12 +27,12 @@ cr.define('print_preview.ticket_items', function() { /** @override */ isCapabilityAvailable() { - var capability = this.capability; + const capability = this.capability; if (!capability) { return false; } - var hasColor = false; - var hasMonochrome = false; + let hasColor = false; + let hasMonochrome = false; capability.option.forEach(function(option) { hasColor = hasColor || (Color.COLOR_TYPES_.indexOf(option.type) >= 0); hasMonochrome = hasMonochrome || @@ -43,7 +43,7 @@ cr.define('print_preview.ticket_items', function() { /** @return {Object} Color capability of the selected destination. */ get capability() { - var dest = this.getSelectedDestInternal(); + const dest = this.getSelectedDestInternal(); return (dest && dest.capabilities && dest.capabilities.printer && dest.capabilities.printer.color) || null; @@ -51,13 +51,13 @@ cr.define('print_preview.ticket_items', function() { /** @return {Object} Color option corresponding to the current value. */ getSelectedOption() { - var capability = this.capability; - var options = capability ? capability.option : null; + const capability = this.capability; + const options = capability ? capability.option : null; if (options) { - var typesToLookFor = + const typesToLookFor = this.getValue() ? Color.COLOR_TYPES_ : Color.MONOCHROME_TYPES_; - for (var i = 0; i < typesToLookFor.length; i++) { - var matchingOptions = options.filter(function(option) { + for (let i = 0; i < typesToLookFor.length; i++) { + const matchingOptions = options.filter(function(option) { return option.type == typesToLookFor[i]; }); if (matchingOptions.length > 0) { @@ -70,8 +70,8 @@ cr.define('print_preview.ticket_items', function() { /** @override */ getDefaultValueInternal() { - var capability = this.capability; - var defaultOption = + const capability = this.capability; + const defaultOption = capability ? this.getDefaultColorOption_(capability.option) : null; return defaultOption && (Color.COLOR_TYPES_.indexOf(defaultOption.type) >= 0); @@ -82,7 +82,7 @@ cr.define('print_preview.ticket_items', function() { // 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'. - var dest = this.getSelectedDestInternal(); + const dest = this.getSelectedDestInternal(); if (dest) { if (dest.id == print_preview.Destination.GooglePromotedId.DOCS || dest.type == print_preview.DestinationType.MOBILE) { @@ -101,7 +101,7 @@ cr.define('print_preview.ticket_items', function() { * @private */ getDefaultColorOption_(options) { - var defaultOptions = options.filter(function(option) { + const defaultOptions = options.filter(function(option) { return option.is_default; }); return (defaultOptions.length == 0) ? null : defaultOptions[0]; 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 84f20a7cb2f..560e54b2d52 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 @@ -29,13 +29,13 @@ cr.define('print_preview.ticket_items', function() { /** @return {number} The number of copies indicated by the ticket item. */ getValueAsNumber() { - var value = this.getValue(); + const value = this.getValue(); return value == '' ? 0 : parseInt(value, 10); } /** @override */ getDefaultValueInternal() { - var cap = this.getCopiesCapability_(); + const cap = this.getCopiesCapability_(); return cap.hasOwnProperty('default') ? cap.default : '1'; } @@ -49,7 +49,7 @@ cr.define('print_preview.ticket_items', function() { * @private */ getCopiesCapability_() { - var dest = this.getSelectedDestInternal(); + const dest = this.getSelectedDestInternal(); return (dest && dest.capabilities && dest.capabilities.printer && dest.capabilities.printer.copies) || null; 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 a29bc892378..4b2579bd2a8 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 @@ -2,23 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -cr.exportPath('print_preview.ticket_items'); - -/** - * Enumeration of the orientations of margins. - * @enum {string} - */ -print_preview.ticket_items.CustomMarginsOrientation = { - TOP: 'top', - RIGHT: 'right', - BOTTOM: 'bottom', - LEFT: 'left' -}; - cr.define('print_preview.ticket_items', function() { 'use strict'; - var CustomMarginsOrientation = + const CustomMarginsOrientation = print_preview.ticket_items.CustomMarginsOrientation; class CustomMargins extends print_preview.ticket_items.TicketItem { @@ -38,10 +25,10 @@ cr.define('print_preview.ticket_items', function() { /** @override */ wouldValueBeValid(value) { - var margins = /** @type {!print_preview.Margins} */ (value); - for (var key in CustomMarginsOrientation) { - var o = CustomMarginsOrientation[key]; - var max = this.getMarginMax_( + const margins = /** @type {!print_preview.Margins} */ (value); + for (const key in CustomMarginsOrientation) { + const o = CustomMarginsOrientation[key]; + const max = this.getMarginMax_( o, margins.get(CustomMargins.OppositeOrientation_[o])); if (margins.get(o) > max || margins.get(o) < 0) { return false; @@ -66,14 +53,14 @@ cr.define('print_preview.ticket_items', function() { * @return {number} Maximum value in points of the specified margin. */ getMarginMax(orientation) { - var oppositeOrient = CustomMargins.OppositeOrientation_[orientation]; - var margins = /** @type {!print_preview.Margins} */ (this.getValue()); + const oppositeOrient = CustomMargins.OppositeOrientation_[orientation]; + const margins = /** @type {!print_preview.Margins} */ (this.getValue()); return this.getMarginMax_(orientation, margins.get(oppositeOrient)); } /** @override */ updateValue(value) { - var margins = /** @type {!print_preview.Margins} */ (value); + let margins = /** @type {!print_preview.Margins} */ (value); if (margins != null) { margins = new print_preview.Margins( Math.round(margins.get(CustomMarginsOrientation.TOP)), @@ -93,9 +80,10 @@ cr.define('print_preview.ticket_items', function() { * @param {number} value Updated margin value in points. */ updateMargin(orientation, value) { - var margins = /** @type {!print_preview.Margins} */ (this.getValue()); - var oppositeOrientation = CustomMargins.OppositeOrientation_[orientation]; - var max = + const margins = /** @type {!print_preview.Margins} */ (this.getValue()); + const oppositeOrientation = + CustomMargins.OppositeOrientation_[orientation]; + const max = this.getMarginMax_(orientation, margins.get(oppositeOrientation)); value = Math.max(0, Math.min(max, value)); this.updateValue(margins.set(orientation, value)); @@ -122,12 +110,12 @@ cr.define('print_preview.ticket_items', function() { * @private */ getMarginMax_(orientation, oppositeMargin) { - var dimensionLength = (orientation == CustomMarginsOrientation.TOP || - orientation == CustomMarginsOrientation.BOTTOM) ? + const dimensionLength = (orientation == CustomMarginsOrientation.TOP || + orientation == CustomMarginsOrientation.BOTTOM) ? this.getDocumentInfoInternal().pageSize.height : this.getDocumentInfoInternal().pageSize.width; - var totalMargin = + const totalMargin = dimensionLength - CustomMargins.MINIMUM_MARGINS_DISTANCE_; return Math.round(totalMargin > 0 ? totalMargin - oppositeMargin : 0); } 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 218c7cc9b8f..60c401ac920 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 @@ -36,7 +36,7 @@ cr.define('print_preview.ticket_items', function() { /** @override */ isValueEqual(value) { - var myValue = this.getValue(); + const myValue = this.getValue(); return myValue.horizontal_dpi == value.horizontal_dpi && myValue.vertical_dpi == value.vertical_dpi && myValue.vendor_id == value.vendor_id; @@ -44,7 +44,7 @@ cr.define('print_preview.ticket_items', function() { /** @return {Object} DPI capability of the selected destination. */ get capability() { - var destination = this.getSelectedDestInternal(); + const destination = this.getSelectedDestInternal(); return (destination && destination.capabilities && destination.capabilities.printer && destination.capabilities.printer.dpi) || @@ -53,7 +53,7 @@ cr.define('print_preview.ticket_items', function() { /** @override */ getDefaultValueInternal() { - var defaultOptions = this.capability.option.filter(function(option) { + const defaultOptions = this.capability.option.filter(function(option) { return option.is_default; }); return defaultOptions.length > 0 ? defaultOptions[0] : null; 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 6d061d70360..e7c63426a2b 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 @@ -27,12 +27,12 @@ cr.define('print_preview.ticket_items', function() { /** @override */ isCapabilityAvailable() { - var cap = this.getDuplexCapability_(); + const cap = this.getDuplexCapability_(); if (!cap) { return false; } - var hasLongEdgeOption = false; - var hasSimplexOption = false; + let hasLongEdgeOption = false; + let hasSimplexOption = false; cap.option.forEach(function(option) { hasLongEdgeOption = hasLongEdgeOption || option.type == 'LONG_EDGE'; hasSimplexOption = hasSimplexOption || option.type == 'NO_DUPLEX'; @@ -42,8 +42,8 @@ cr.define('print_preview.ticket_items', function() { /** @override */ getDefaultValueInternal() { - var cap = this.getDuplexCapability_(); - var defaultOptions = cap.option.filter(function(option) { + const cap = this.getDuplexCapability_(); + const defaultOptions = cap.option.filter(function(option) { return option.is_default; }); return defaultOptions.length == 0 ? false : @@ -60,7 +60,7 @@ cr.define('print_preview.ticket_items', function() { * @private */ getDuplexCapability_() { - var dest = this.getSelectedDestInternal(); + const dest = this.getSelectedDestInternal(); return (dest && dest.capabilities && dest.capabilities.printer && dest.capabilities.printer.duplex) || null; 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 f69389f27c9..77d29c8e755 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 @@ -70,7 +70,7 @@ cr.define('print_preview.ticket_items', function() { print_preview.ticket_items.MarginsTypeValue.NO_MARGINS) { return false; } - var microns = this.landscape_.getValue() ? + const microns = this.landscape_.getValue() ? this.mediaSize_.getValue().width_microns : this.mediaSize_.getValue().height_microns; if (microns < HeaderFooter.MINIMUM_HEIGHT_MICRONS_) { @@ -82,7 +82,7 @@ cr.define('print_preview.ticket_items', function() { print_preview.ticket_items.MarginsTypeValue.MINIMUM) { return true; } - var margins; + let margins; if (this.marginsType_.getValue() == print_preview.ticket_items.MarginsTypeValue.CUSTOM) { if (!this.customMargins_.isValid()) { @@ -92,7 +92,7 @@ cr.define('print_preview.ticket_items', function() { } else { margins = this.getDocumentInfoInternal().margins; } - var orientEnum = print_preview.ticket_items.CustomMarginsOrientation; + const orientEnum = print_preview.ticket_items.CustomMarginsOrientation; return margins == null || margins.get(orientEnum.TOP) > 0 || margins.get(orientEnum.BOTTOM) > 0; } 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 5c8ef4f6438..8fe4850203c 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 @@ -49,11 +49,11 @@ cr.define('print_preview.ticket_items', function() { /** @override */ isCapabilityAvailable() { - var cap = this.getPageOrientationCapability_(); + const cap = this.getPageOrientationCapability_(); if (!cap) return false; - var hasAutoOrPortraitOption = false; - var hasLandscapeOption = false; + let hasAutoOrPortraitOption = false; + let hasLandscapeOption = false; cap.option.forEach(function(option) { hasAutoOrPortraitOption = hasAutoOrPortraitOption || option.type == 'AUTO' || option.type == 'PORTRAIT'; @@ -70,8 +70,8 @@ cr.define('print_preview.ticket_items', function() { /** @override */ getDefaultValueInternal() { - var cap = this.getPageOrientationCapability_(); - var defaultOptions = cap.option.filter(function(option) { + const cap = this.getPageOrientationCapability_(); + const defaultOptions = cap.option.filter(function(option) { return option.is_default; }); return defaultOptions.length == 0 ? false : @@ -80,7 +80,7 @@ cr.define('print_preview.ticket_items', function() { /** @override */ getCapabilityNotAvailableValueInternal() { - var doc = this.getDocumentInfoInternal(); + const doc = this.getDocumentInfoInternal(); return doc.hasCssMediaStyles ? (doc.pageSize.width > doc.pageSize.height) : false; @@ -88,7 +88,7 @@ cr.define('print_preview.ticket_items', function() { /** @override */ updateValueInternal(value) { - var updateMargins = !this.isValueEqual(value); + const updateMargins = !this.isValueEqual(value); print_preview.ticket_items.TicketItem.prototype.updateValueInternal.call( this, value); if (updateMargins) { @@ -104,7 +104,7 @@ cr.define('print_preview.ticket_items', function() { * @param {string} value Option to check. */ hasOption(value) { - var cap = this.getPageOrientationCapability_(); + const cap = this.getPageOrientationCapability_(); if (!cap) return false; return cap.option.some(function(option) { @@ -117,7 +117,7 @@ cr.define('print_preview.ticket_items', function() { * @private */ getPageOrientationCapability_() { - var dest = this.getSelectedDestInternal(); + const dest = this.getSelectedDestInternal(); return (dest && dest.capabilities && dest.capabilities.printer && dest.capabilities.printer.page_orientation) || null; 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 8f49b7eaaba..6302e0b418d 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 @@ -54,7 +54,7 @@ cr.define('print_preview.ticket_items', function() { /** @override */ isCapabilityAvailable() { - var knownSizeToSaveAsPdf = + const knownSizeToSaveAsPdf = (!this.getDocumentInfoInternal().isModifiable || this.getDocumentInfoInternal().hasCssMediaStyles) && this.getSelectedDestInternal() && @@ -65,7 +65,7 @@ cr.define('print_preview.ticket_items', function() { /** @override */ isValueEqual(value) { - var myValue = this.getValue(); + const myValue = this.getValue(); return myValue.width_microns == value.width_microns && myValue.height_microns == value.height_microns && myValue.is_continuous_feed == value.is_continuous_feed && @@ -74,7 +74,7 @@ cr.define('print_preview.ticket_items', function() { /** @return {Object} Media size capability of the selected destination. */ get capability() { - var destination = this.getSelectedDestInternal(); + const destination = this.getSelectedDestInternal(); return (destination && destination.capabilities && destination.capabilities.printer && destination.capabilities.printer.media_size) || @@ -83,7 +83,7 @@ cr.define('print_preview.ticket_items', function() { /** @override */ getDefaultValueInternal() { - var defaultOptions = this.capability.option.filter(function(option) { + const defaultOptions = this.capability.option.filter(function(option) { return option.is_default; }); return defaultOptions.length > 0 ? defaultOptions[0] : null; @@ -96,7 +96,7 @@ cr.define('print_preview.ticket_items', function() { /** @override */ updateValueInternal(value) { - var updateMargins = !this.isValueEqual(value); + const updateMargins = !this.isValueEqual(value); print_preview.ticket_items.TicketItem.prototype.updateValueInternal.call( this, value); if (updateMargins) { 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 23de286cfe4..ab4a9c81c05 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 @@ -20,7 +20,7 @@ cr.define('print_preview.ticket_items', function() { /** @override */ wouldValueBeValid(value) { - var result = pageRangeTextToPageRanges( + const result = pageRangeTextToPageRanges( value, this.getDocumentInfoInternal().pageCount); return Array.isArray(result); } @@ -30,7 +30,7 @@ cr.define('print_preview.ticket_items', function() { * page range string. */ getPageNumberSet() { - var pageNumberList = pageRangeTextToPageList( + const pageNumberList = pageRangeTextToPageList( this.getValueAsString_(), this.getDocumentInfoInternal().pageCount); return new print_preview.PageNumberSet(pageNumberList); } @@ -63,7 +63,7 @@ cr.define('print_preview.ticket_items', function() { * ranges. */ getPageRanges() { - var pageRanges = pageRangeTextToPageRanges(this.getValueAsString_()); + const pageRanges = pageRangeTextToPageRanges(this.getValueAsString_()); return Array.isArray(pageRanges) ? pageRanges : []; } @@ -74,7 +74,7 @@ cr.define('print_preview.ticket_items', function() { * page ranges. */ getDocumentPageRanges() { - var pageRanges = pageRangeTextToPageRanges( + const pageRanges = pageRangeTextToPageRanges( this.getValueAsString_(), this.getDocumentInfoInternal().pageCount); return Array.isArray(pageRanges) ? pageRanges : []; } @@ -90,7 +90,7 @@ cr.define('print_preview.ticket_items', function() { * @return {!PageRangeStatus} */ checkValidity() { - var pageRanges = pageRangeTextToPageRanges( + const pageRanges = pageRangeTextToPageRanges( this.getValueAsString_(), this.getDocumentInfoInternal().pageCount); return Array.isArray(pageRanges) ? PageRangeStatus.NO_ERROR : pageRanges; } 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 6ecc989d0e1..ab890bc9326 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 @@ -37,7 +37,7 @@ cr.define('print_preview.ticket_items', 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 = + const knownSizeToSaveAsPdf = (!this.getDocumentInfoInternal().isModifiable || this.getDocumentInfoInternal().hasCssMediaStyles) && this.getSelectedDestInternal() && @@ -48,7 +48,7 @@ cr.define('print_preview.ticket_items', function() { /** @return {number} The scaling percentage indicated by the ticket item. */ getValueAsNumber() { - var value = this.getValue() == '' ? 0 : parseInt(this.getValue(), 10); + const value = this.getValue() == '' ? 0 : parseInt(this.getValue(), 10); assert(!isNaN(value)); return value; } 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 9d543289ea7..c402eab30fb 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 @@ -131,7 +131,7 @@ cr.define('print_preview.ticket_items', function() { */ updateValue(value) { // Use comparison with capabilities for event. - var sendUpdateEvent = !this.isValueEqual(value); + const sendUpdateEvent = !this.isValueEqual(value); // Don't lose requested value if capability is not available. this.updateValueInternal(value); if (this.appState_ && (this.field_ != null) && 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 0bdcd9f9e5c..8b3342445c5 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 @@ -49,7 +49,7 @@ cr.define('print_preview.ticket_items', function() { /** @return {boolean} Whether the ticket item was modified by the user. */ isUserEdited() { // If there's at least one ticket item stored in values, it was edited. - for (var key in this.items_) { + for (const key in this.items_) { if (this.items_.hasOwnProperty(key)) return true; } @@ -58,7 +58,7 @@ cr.define('print_preview.ticket_items', function() { /** @return {Object} Vendor capabilities of the selected destination. */ get capability() { - var destination = this.destinationStore_ ? + const destination = this.destinationStore_ ? this.destinationStore_.selectedDestination : null; if (!destination) @@ -86,7 +86,7 @@ cr.define('print_preview.ticket_items', function() { updateValue(values) { this.items_ = {}; if (typeof values == 'object') { - for (var key in values) { + for (const key in values) { if (values.hasOwnProperty(key) && typeof values[key] == 'string') { // Let's empirically limit each value at 2K. this.items_[key] = values[key].substring(0, 2048); diff --git a/chromium/chrome/browser/resources/print_preview/data/user_info.js b/chromium/chrome/browser/resources/print_preview/data/user_info.js index 5076052302c..46d62bb7622 100644 --- a/chromium/chrome/browser/resources/print_preview/data/user_info.js +++ b/chromium/chrome/browser/resources/print_preview/data/user_info.js @@ -5,51 +5,38 @@ cr.define('print_preview', function() { 'use strict'; - /** - * Repository which stores information about the user. Events are dispatched - * when the information changes. - * @constructor - * @extends {cr.EventTarget} - */ - function UserInfo() { - cr.EventTarget.call(this); - + class UserInfo extends cr.EventTarget { /** - * Email address of the logged in user or {@code null} if no user is logged - * in. In case of Google multilogin, can be changed by the user. - * @private {?string} + * Repository which stores information about the user. Events are dispatched + * when the information changes. */ - this.activeUser_ = null; - - /** - * Email addresses of the logged in users or empty array if no user is - * logged in. {@code null} if not known yet. - * @private {?Array<string>} - */ - this.users_ = null; - } + constructor() { + super(); - /** - * Enumeration of event types dispatched by the user info. - * @enum {string} - */ - UserInfo.EventType = { - ACTIVE_USER_CHANGED: 'print_preview.UserInfo.ACTIVE_USER_CHANGED', - USERS_CHANGED: 'print_preview.UserInfo.USERS_CHANGED' - }; + /** + * Email address of the logged in user or {@code null} if no user is + * logged in. In case of Google multilogin, can be changed by the user. + * @private {?string} + */ + this.activeUser_ = null; - UserInfo.prototype = { - __proto__: cr.EventTarget.prototype, + /** + * Email addresses of the logged in users or empty array if no user is + * logged in. {@code null} if not known yet. + * @private {?Array<string>} + */ + this.users_ = null; + } /** @return {boolean} Whether user accounts are already retrieved. */ get initialized() { return this.users_ != null; - }, + } /** @return {boolean} Whether user is logged in or not. */ get loggedIn() { return !!this.activeUser; - }, + } /** * @return {?string} Email address of the logged in user or {@code null} if @@ -57,15 +44,19 @@ cr.define('print_preview', function() { */ get activeUser() { return this.activeUser_; - }, + } - /** Changes active user. */ + /** + * Changes active user. + * @param {?string} activeUser Email address for the user to be set as + * active. + */ set activeUser(activeUser) { - if (this.activeUser_ != activeUser) { + if (!!activeUser && this.activeUser_ != activeUser) { this.activeUser_ = activeUser; cr.dispatchSimpleEvent(this, UserInfo.EventType.ACTIVE_USER_CHANGED); } - }, + } /** * @return {?Array<string>} Email addresses of the logged in users or @@ -73,18 +64,27 @@ cr.define('print_preview', function() { */ get users() { return this.users_; - }, + } /** * Sets logged in user accounts info. * @param {string} activeUser Active user account (email). * @param {!Array<string>} users List of currently logged in accounts. */ - setUsers: function(activeUser, users) { + setUsers(activeUser, users) { this.activeUser_ = activeUser; this.users_ = users || []; cr.dispatchSimpleEvent(this, UserInfo.EventType.USERS_CHANGED); - }, + } + } + + /** + * Enumeration of event types dispatched by the user info. + * @enum {string} + */ + UserInfo.EventType = { + ACTIVE_USER_CHANGED: 'print_preview.UserInfo.ACTIVE_USER_CHANGED', + USERS_CHANGED: 'print_preview.UserInfo.USERS_CHANGED' }; return {UserInfo: UserInfo}; diff --git a/chromium/chrome/browser/resources/print_preview/native_layer.js b/chromium/chrome/browser/resources/print_preview/native_layer.js index 3aaa9947da1..e79740216ed 100644 --- a/chromium/chrome/browser/resources/print_preview/native_layer.js +++ b/chromium/chrome/browser/resources/print_preview/native_layer.js @@ -197,8 +197,8 @@ cr.define('print_preview', function() { */ getNativeColorModel_(destination, color) { // For non-local printers native color model is ignored anyway. - var option = destination.isLocal ? color.getSelectedOption() : null; - var nativeColorModel = parseInt(option ? option.vendor_id : null, 10); + const option = destination.isLocal ? color.getSelectedOption() : null; + const nativeColorModel = parseInt(option ? option.vendor_id : null, 10); if (isNaN(nativeColorModel)) { return color.getValue() ? NativeLayer.ColorMode_.COLOR : NativeLayer.ColorMode_.GRAY; @@ -228,7 +228,7 @@ cr.define('print_preview', function() { printTicketStore.isTicketValidForPreview(), 'Trying to generate preview when ticket is not valid'); - var ticket = { + const ticket = { 'pageRange': printTicketStore.pageRange.getDocumentPageRanges(), 'mediaSize': printTicketStore.mediaSize.getValue(), 'landscape': printTicketStore.landscape.getValue(), @@ -274,8 +274,8 @@ cr.define('print_preview', function() { if (printTicketStore.marginsType.isCapabilityAvailable() && printTicketStore.marginsType.getValue() == print_preview.ticket_items.MarginsTypeValue.CUSTOM) { - var customMargins = printTicketStore.customMargins.getValue(); - var orientationEnum = + const customMargins = printTicketStore.customMargins.getValue(); + const orientationEnum = print_preview.ticket_items.CustomMarginsOrientation; ticket['marginsCustom'] = { 'marginTop': customMargins.get(orientationEnum.TOP), @@ -295,8 +295,6 @@ cr.define('print_preview', function() { * @param {!print_preview.Destination} destination Destination to print to. * @param {!print_preview.PrintTicketStore} printTicketStore Used to get the * state of the print ticket. - * @param {cloudprint.CloudPrintInterface} cloudPrintInterface Interface - * to Google Cloud Print. * @param {!print_preview.DocumentInfo} documentInfo Document data model. * @param {boolean=} opt_isOpenPdfInPreview Whether to open the PDF in the * system's preview application. @@ -306,8 +304,8 @@ cr.define('print_preview', function() { * finished or rejected. */ print( - destination, printTicketStore, cloudPrintInterface, documentInfo, - opt_isOpenPdfInPreview, opt_showSystemDialog) { + destination, printTicketStore, documentInfo, opt_isOpenPdfInPreview, + opt_showSystemDialog) { assert( printTicketStore.isTicketValid(), 'Trying to print when ticket is not valid'); @@ -316,7 +314,10 @@ cr.define('print_preview', function() { !opt_showSystemDialog || (cr.isWindows && destination.isLocal), 'Implemented for Windows only'); - var ticket = { + // Note: update + // chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc + // with any changes to ticket creation. + const ticket = { 'mediaSize': printTicketStore.mediaSize.getValue(), 'pageCount': printTicketStore.pageRange.getPageNumberSet().size, 'landscape': printTicketStore.landscape.getValue(), @@ -361,8 +362,8 @@ cr.define('print_preview', function() { if (printTicketStore.marginsType.isCapabilityAvailable() && printTicketStore.marginsType.isValueEqual( print_preview.ticket_items.MarginsTypeValue.CUSTOM)) { - var customMargins = printTicketStore.customMargins.getValue(); - var orientationEnum = + const customMargins = printTicketStore.customMargins.getValue(); + const orientationEnum = print_preview.ticket_items.CustomMarginsOrientation; ticket['marginsCustom'] = { 'marginTop': customMargins.get(orientationEnum.TOP), @@ -433,19 +434,12 @@ cr.define('print_preview', function() { return cr.sendWithPromise('signIn', addAccount); } - /** Navigates the user to the system printer settings interface. */ - manageLocalPrinters() { - chrome.send('manageLocalPrinters'); - } - /** - * Navigates the user to the Google Cloud Print management page. - * @param {?string} user Email address of the user to open the management - * page for (user must be currently logged in, indeed) or {@code null} - * to open this page for the primary user. + * Navigates the user to the Chrome printing setting page to manage local + * printers and Google cloud printers. */ - manageCloudPrinters(user) { - chrome.send('manageCloudPrinters', [user || '']); + managePrinters() { + chrome.send('managePrinters'); } /** Forces browser to open a new tab with the given URL address. */ @@ -471,14 +465,6 @@ cr.define('print_preview', function() { } /** - * Notifies the metrics handler to record an action. - * @param {string} action The action to record. - */ - recordAction(action) { - chrome.send('metricsHandler:recordAction', [action]); - } - - /** * Notifies the metrics handler to record a histogram value. * @param {string} histogram The name of the histogram to record * @param {number} bucket The bucket to record @@ -491,7 +477,7 @@ cr.define('print_preview', function() { } /** @private {?print_preview.NativeLayer} */ - var currentInstance = null; + let currentInstance = null; /** * Constant values matching printing::DuplexMode enum. diff --git a/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.html b/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.html new file mode 100644 index 00000000000..554ba1337a4 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.html @@ -0,0 +1,23 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="button_css.html"> +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="settings_section.html"> + +<dom-module id="print-preview-advanced-options-settings"> + <template> + <style include="print-preview-shared button"> + :host button { + min-height: 28px; + width: 100%; + } + </style> + <print-preview-settings-section> + <span slot="title">$i18n{advancedOptionsLabel}</span> + <div slot="controls"> + <button>$i18n{showAdvancedOptions}</button> + </div> + </print-preview-settings-section> + </template> + <script src="advanced_options_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.js b/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.js new file mode 100644 index 00000000000..6df05eb6896 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.js @@ -0,0 +1,7 @@ +// 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. + +Polymer({ + is: 'print-preview-advanced-options-settings', +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/app.html b/chromium/chrome/browser/resources/print_preview/new/app.html new file mode 100644 index 00000000000..bde13b165c8 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/app.html @@ -0,0 +1,95 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="model.html"> +<link rel="import" href="header.html"> +<link rel="import" href="destination_settings.html"> +<link rel="import" href="pages_settings.html"> +<link rel="import" href="copies_settings.html"> +<link rel="import" href="layout_settings.html"> +<link rel="import" href="color_settings.html"> +<link rel="import" href="media_size_settings.html"> +<link rel="import" href="margins_settings.html"> +<link rel="import" href="dpi_settings.html"> +<link rel="import" href="scaling_settings.html"> +<link rel="import" href="other_options_settings.html"> +<link rel="import" href="advanced_options_settings.html"> + +<dom-module id="print-preview-app"> + <template> + <style> + :host { + display: flex; + height: 100%; + } + + #sidebar { + -webkit-border-end: 1px solid #c8c8c8; + background-color: white; + display: flex; + flex-direction: column; + max-width: 310px; + min-width: 310px; + } + + #settings-sections { + background: #fbfbfb; + border-top: 1px solid #f3f3f3; + flex: 1; + overflow: auto; + } + + #previewArea { + -webkit-border-start: 1px solid #dcdcdc; + align-items: center; + background-color: #e6e6e6; + display: flex; + flex: 1; + justify-content: center; + } + </style> + <print-preview-model id="model" settings="{{settings}}" + destination="{{destination}}" document-info="{{documentInfo}}" + state="{{state}}"></print-preview-model> + <div id="sidebar"> + <print-preview-header destination="[[destination]]" state="[[state]]" + settings="[[settings]]"></print-preview-header> + <div id="settings-sections"> + <print-preview-destination-settings destination="[[destination]]"> + </print-preview-destination-settings> + <print-preview-pages-settings settings="{{settings}}" + document-info="[[documentInfo]]" + hidden$="[[!settings.pages.available]]"> + </print-preview-pages-settings> + <print-preview-copies-settings settings="{{settings}}" + hidden$="[[!settings.copies.available]]"> + </print-preview-copies-settings> + <print-preview-layout-settings settings="{{settings}}" + hidden$="[[!settings.layout.available]]"> + </print-preview-layout-settings> + <print-preview-color-settings settings="{{settings}}" + hidden$="[[!settings.color.available]]"> + </print-preview-color-settings> + <print-preview-media-size-settings settings="{{settings}}" + hidden$="[[!settings.mediaSize.available]]"> + </print-preview-media-size-settings> + <print-preview-margins-settings settings="{{settings}}" + hidden$="[[!settings.margins.available]]"> + </print-preview-margins-settings> + <print-preview-dpi-settings settings="{{settings}}" + hidden$="[[!settings.dpi.available]]"> + </print-preview-dpi-settings> + <print-preview-scaling-settings settings="{{settings}}" + document-info="[[documentInfo]]" + hidden$="[[!settings.scaling.available]]"> + </print-preview-scaling-settings> + <print-preview-other-options-settings settings="{{settings}}"> + </print-preview-other-options-settings> + <print-preview-advanced-options-settings settings="{{settings}}" + hidden$="[[!settings.vendorItems.available]]"> + </print-preview-advanced-options-settings> + </div> + </div> + <div id="previewArea">preview area</div> + </template> + <script src="app.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/app.js b/chromium/chrome/browser/resources/print_preview/new/app.js new file mode 100644 index 00000000000..9ca31b13b63 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/app.js @@ -0,0 +1,37 @@ +// 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. + +Polymer({ + is: 'print-preview-app', + + properties: { + /** + * Object containing current settings of Print Preview, for use by Polymer + * controls. + * @type {!Object} + */ + settings: { + type: Object, + notify: true, + }, + + /** @type {print_preview.Destination} */ + destination: { + type: Object, + notify: true, + }, + + /** @type {print_preview.DocumentInfo} */ + documentInfo: { + type: Object, + notify: true, + }, + + /** @type {!print_preview_new.State} */ + state: { + type: Object, + notify: true, + }, + }, +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/button_css.html b/chromium/chrome/browser/resources/print_preview/new/button_css.html new file mode 100644 index 00000000000..d33555445a4 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/button_css.html @@ -0,0 +1,26 @@ +<!-- Styles copied from resources/css/widgets.css. --> +<link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="print_preview_shared_css.html"> + +<dom-module id="button"> + <template> + <style include="print-preview-shared"> + button { + -webkit-padding-end: 10px; + -webkit-padding-start: 10px; + } + + <if expr="not is_ios"> + button:enabled:hover { + background-image: linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0); + @apply(--print-preview-hover); + } + </if> + + button:enabled:active { + background-image: linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7); + @apply(--print-preview-active); + } + </style> + </template> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/checkbox_radio_css.html b/chromium/chrome/browser/resources/print_preview/new/checkbox_radio_css.html new file mode 100644 index 00000000000..f2564079cbd --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/checkbox_radio_css.html @@ -0,0 +1,123 @@ +<!-- Styles copied from resources/css/widgets.css. --> +<link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="print_preview_shared_css.html"> + +<dom-module id="checkbox-radio"> + <template> + <style include="print-preview-shared"> + input[type='checkbox'] { + height: 13px; + position: relative; + vertical-align: middle; + width: 13px; + } + + input[type='radio'] { + /* OVERRIDE */ + border-radius: 100%; + height: 15px; + position: relative; + vertical-align: middle; + width: 15px; + } + + input[type='checkbox']:checked::before { + background-image: url(chrome://resources/images/check.png); + background-size: 100% 100%; + content: ''; + display: block; + height: 100%; + user-select: none; + width: 100%; + } + + input[type='radio']:checked::before { + background-color: #666; + border-radius: 100%; + bottom: 3px; + content: ''; + display: block; + left: 3px; + position: absolute; + right: 3px; + top: 3px; + } + + <if expr="not is_ios"> + input:enabled:hover:-webkit-any([type='checkbox'], + [type='radio']) { + background-image: + linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0); + @apply(--print-preview-hover); + } + </if> + + input:enabled:active:-webkit-any([type='checkbox'], + [type='radio']) { + background-image: linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7); + @apply(--print-preview-active); + } + + input:disabled:-webkit-any([type='checkbox'], + [type='radio']) { + opacity: .75; + } + + /* Checkbox/radio helpers *********************************************** + * + * .checkbox and .radio classes wrap labels. Checkboxes and radios should + * use these classes with the markup structure: + * + * <div class="checkbox"> + * <label> + * <input type="checkbox"> + * <span> + * </label> + * </div> + */ + + :-webkit-any(.checkbox, .radio) label { + /* Don't expand horizontally: <http://crbug.com/112091>. */ + align-items: center; + display: flex; + user-select: none; + } + + .radio label { + padding-bottom: 5px; + padding-top: 10px; + } + + .checkbox label { + padding-bottom: 7px; + padding-top: 7px; + } + + :-webkit-any(.checkbox, .radio) label input { + flex-shrink: 0; + } + + :-webkit-any(.checkbox, .radio) label input ~ span { + -webkit-margin-start: 0.6em; + /* Make sure long spans wrap at the same horizontal position they + * start. + */ + display: block; + } + + :-webkit-any(.checkbox, .radio) label:hover { + color: black; + } + + :host .radio input[type='radio'], + :host label input[type='checkbox'] { + --min-size: 13.19px; + --size: 1.1em; + height: var(--size); + min-height: var(--min-size); + min-width: var(--min-size); + width: var(--size); + } + </style> + </template> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/color_settings.html b/chromium/chrome/browser/resources/print_preview/new/color_settings.html new file mode 100644 index 00000000000..cca961b8623 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/color_settings.html @@ -0,0 +1,22 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="select_css.html"> +<link rel="import" href="settings_section.html"> + +<dom-module id="print-preview-color-settings"> + <template> + <style include="print-preview-shared select"> + </style> + <print-preview-settings-section> + <span id="color-label" slot="title">$i18n{optionColor}</span> + <div slot="controls"> + <select aria-labelledby="color-label"> + <option value="bw" selected>$i18n{optionBw}</option> + <option value="color">$i18n{optionColor}</option> + </select> + </div> + </print-preview-settings-section> + </template> + <script src="color_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/color_settings.js b/chromium/chrome/browser/resources/print_preview/new/color_settings.js new file mode 100644 index 00000000000..b28f250afeb --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/color_settings.js @@ -0,0 +1,7 @@ +// 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. + +Polymer({ + is: 'print-preview-color-settings', +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/compiled_resources2.gyp b/chromium/chrome/browser/resources/print_preview/new/compiled_resources2.gyp new file mode 100644 index 00000000000..d8e8670920c --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/compiled_resources2.gyp @@ -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. +{ + 'targets': [ + { + 'target_name': 'app', + 'dependencies': [ + 'header', + 'destination_settings', + 'pages_settings', + 'copies_settings', + 'layout_settings', + 'color_settings', + 'media_size_settings', + 'margins_settings', + 'dpi_settings', + 'scaling_settings', + 'other_options_settings', + 'advanced_options_settings', + 'model', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'header', + 'dependencies': [ + '../data/compiled_resources2.gyp:destination', + 'settings_behavior', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'destination_settings', + 'dependencies': [ + '../data/compiled_resources2.gyp:destination', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'pages_settings', + 'dependencies': [ + 'settings_behavior', + '../data/compiled_resources2.gyp:document_info', + '<(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': 'copies_settings', + 'dependencies': [ + 'number_settings_section', + 'settings_behavior', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'layout_settings', + 'dependencies': [ + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'color_settings', + 'dependencies': [ + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'media_size_settings', + 'dependencies': [ + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'margins_settings', + 'dependencies': [ + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'dpi_settings', + 'dependencies': [ + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'scaling_settings', + 'dependencies': [ + '../data/compiled_resources2.gyp:document_info', + 'number_settings_section', + 'settings_behavior', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'other_options_settings', + 'dependencies': [ + 'settings_behavior', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'advanced_options_settings', + 'dependencies': [ + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'number_settings_section', + 'dependencies': [ + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'settings_behavior', + 'dependencies': [ + 'model', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'model', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '../data/compiled_resources2.gyp:destination', + '../data/compiled_resources2.gyp:document_info', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + } + ], +} diff --git a/chromium/chrome/browser/resources/print_preview/new/copies_settings.html b/chromium/chrome/browser/resources/print_preview/new/copies_settings.html new file mode 100644 index 00000000000..f6d9374fe18 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/copies_settings.html @@ -0,0 +1,28 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="checkbox_radio_css.html"> +<link rel="import" href="number_settings_section.html"> +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="settings_behavior.html"> + +<dom-module id="print-preview-copies-settings"> + <template> + <style include="print-preview-shared checkbox-radio input"> + </style> + <print-preview-number-settings-section max-value="999" min-value=1 + default-value="1" input-label="$i18n{copiesLabel}" + input-string="{{inputString_}}" input-valid="{{inputValid_}}" + hint-message="$i18n{copiesInstruction}"> + <div slot="opt-inside-content" class="collate-container checkbox" + aria-live="polite" + hidden$="[[collateHidden_(inputString_, inputValid_)]]"> + <label> + <input class="collate" type="checkbox" checked + aria-labelledby="copies-collate-label"> + <span id="copies-collate-label">$i18n{optionCollate}</span> + </label> + </div> + </print-preview-number-settings-section> + </template> + <script src="copies_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/copies_settings.js b/chromium/chrome/browser/resources/print_preview/new/copies_settings.js new file mode 100644 index 00000000000..b58408ff391 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/copies_settings.js @@ -0,0 +1,56 @@ +// 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. + +Polymer({ + is: 'print-preview-copies-settings', + + behaviors: [SettingsBehavior], + + properties: { + /** @private {string} */ + inputString_: String, + + /** @private {boolean} */ + inputValid_: Boolean, + }, + + /** @private {boolean} */ + isInitialized_: false, + + observers: [ + 'onInputChanged_(inputString_, inputValid_)', + 'onInitialized_(settings.copies.value)' + ], + + /** + * Updates the input string when the setting has been initialized. + * @private + */ + onInitialized_: function() { + if (this.isInitialized_) + return; + this.isInitialized_ = true; + const copies = this.getSetting('copies'); + this.set('inputString_', copies.value); + }, + + /** + * Updates model.copies and model.copiesInvalid based on the validity + * and current value of the copies input. + * @private + */ + onInputChanged_: function() { + this.setSetting( + 'copies', this.inputValid_ ? parseInt(this.inputString_, 10) : 1); + this.setSettingValid('copies', this.inputValid_); + }, + + /** + * @return {boolean} Whether collate checkbox should be hidden. + * @private + */ + collateHidden_: function() { + return !this.inputValid_ || parseInt(this.inputString_, 10) == 1; + }, +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/destination_settings.html b/chromium/chrome/browser/resources/print_preview/new/destination_settings.html new file mode 100644 index 00000000000..d2882dde447 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/destination_settings.html @@ -0,0 +1,81 @@ +<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="button_css.html"> +<link rel="import" href="../data/destination.html"> +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="throbber_css.html"> +<link rel="import" href="settings_section.html"> + +<dom-module id="print-preview-destination-settings"> + <template> + <style include="print-preview-shared button throbber cr-hidden-style"> + :host button { + margin-top: 10px; + } + + .throbber { + margin-top: 6px; + } + + .destination-settings-box, + .throbber-container { + align-items: center; + display: flex; + min-height: 28px; + } + + .destination-icon { + -webkit-margin-end: 8px; + height: 24px; + vertical-align: middle; + width: 24px; + } + + .destination-info-wrapper { + display: flex; + flex: 1; + flex-direction: column; + width: 100%; + } + + .destination-info-wrapper > div, + .destination-throbber-name { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .destination-throbber-name, + .destination-name { + font-size: 110%; + } + + .destination-location { + opacity: 0.4; + } + </style> + <print-preview-settings-section class="multirow-controls"> + <span slot="title">$i18n{destinationLabel}</span> + <div slot="controls"> + <div class="throbber-container" hidden="[[!loadingDestination_]]"> + <div class="throbber"></div> + <div class="destination-throbber-name"></div> + </div> + <div class="destination-settings-box" hidden="[[loadingDestination_]]"> + <img class="destination-icon" + src="[[destination.iconUrl]]" alt=""> + <div class="destination-info-wrapper"> + <div class="destination-name">[[destination.id]]</div> + <div class="destination-location">[[destination.hint]]</div> + <div class="destination-offline-status"> + [[destination.offlineStatusText]]</div> + </div> + </div> + <button>$i18n{changeDestination}</button> + </div> + </print-preview-settings-section> + </template> + <script src="destination_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/destination_settings.js b/chromium/chrome/browser/resources/print_preview/new/destination_settings.js new file mode 100644 index 00000000000..6fde6cc0895 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/destination_settings.js @@ -0,0 +1,27 @@ +// 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. + +Polymer({ + is: 'print-preview-destination-settings', + + properties: { + /** @type {!print_preview.Destination} */ + destination: Object, + + /** @private {boolean} */ + loadingDestination_: Boolean, + }, + + /** @override */ + ready: function() { + this.loadingDestination_ = true; + // Simulate transition from spinner to destination. + setTimeout(this.doneLoading_.bind(this), 5000); + }, + + /** @private */ + doneLoading_: function() { + this.loadingDestination_ = false; + }, +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/dpi_settings.html b/chromium/chrome/browser/resources/print_preview/new/dpi_settings.html new file mode 100644 index 00000000000..d7988acd78a --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/dpi_settings.html @@ -0,0 +1,20 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="select_css.html"> +<link rel="import" href="settings_section.html"> + +<dom-module id="print-preview-dpi-settings"> + <template> + <style include="print-preview-shared select"> + </style> + <print-preview-settings-section> + <span id="dpi-label" slot="title">$i18n{dpiLabel}</span> + <div slot="controls"> + <select aria-labelledby="dpi-label"> + </select> + </div> + </print-preview-settings-section> + </template> + <script src="dpi_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/dpi_settings.js b/chromium/chrome/browser/resources/print_preview/new/dpi_settings.js new file mode 100644 index 00000000000..62bf5e2dad2 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/dpi_settings.js @@ -0,0 +1,7 @@ +// 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. + +Polymer({ + is: 'print-preview-dpi-settings', +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/header.html b/chromium/chrome/browser/resources/print_preview/new/header.html new file mode 100644 index 00000000000..e16008da496 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/header.html @@ -0,0 +1,85 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="button_css.html"> +<link rel="import" href="../data/destination.html"> +<link rel="import" href="settings_behavior.html"> +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="strings.html"> + +<dom-module id="print-preview-header"> + <template> + <style include="print-preview-shared button"> + :host { + -webkit-padding-end: 19px; + -webkit-padding-start: 20px; + background-color: #f6f6f6; + border-bottom: 1px solid #d2d2d2; + display: block; + padding-bottom: 20px; + padding-top: 6px; + } + + .title { + color: black; + font-size: 1.25em; + font-weight: normal; + margin: 0; + padding-bottom: 12px; + padding-top: 10px; + } + + .summary { + display: block; + min-height: 34px; + } + + #button-strip { + display: flex; + flex-direction: row; + justify-content: flex-end; + } + + #button-strip button { + -webkit-margin-start: 9px; + display: block; + } + + #button-strip button.loading { + cursor: progress; + } + + #button-strip button.default { + font-kerning: none; + font-weight: bold; + } + + #button-strip button.default:not(:focus):not(:disabled) { + border-color: #808080; + } + + #button-strip button.print:enabled { + background-color: rgb(77, 144, 254); + background-image: + linear-gradient(to bottom, rgb(77, 144, 254), rgb(71, 135, 237)); + border: 1px solid rgb(48, 121, 237); + color: #fff; + text-shadow: 0 1px rgba(0,0,0,0.1); + } + </style> + <h1 class="title">$i18n{title}</h1> + <span class="summary" + aria-label="[[getSummaryLabel_(currentErrorOrState_, labelInfo_)]]" + inner-h-t-m-l="[[getSummary_(currentErrorOrState_, labelInfo_)]]"> + </span> + <div id="button-strip"> + <button class="cancel" on-tap="onCancelButtonTap_"> + $i18n{cancel} + </button> + <button class="print default" on-tap="onPrintButtonTap_" + disabled$="[[printButtonDisabled_(currentErrorOrState_)]]"> + [[getPrintButton_(destination.id)]] + </button> + </div> + </template> + <script src="header.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/header.js b/chromium/chrome/browser/resources/print_preview/new/header.js new file mode 100644 index 00000000000..a9aed406475 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/header.js @@ -0,0 +1,190 @@ +// 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. + +Polymer({ + is: 'print-preview-header', + + behaviors: [SettingsBehavior], + + properties: { + /** @type {!print_preview.Destination} */ + destination: Object, + + /** @type {!print_preview_new.State} */ + state: Object, + + /** @private {boolean} */ + printInProgress_: { + type: Boolean, + notify: true, + value: false, + }, + + /** + * @private {?string} Null value indicates that there is no error or + * state to display in the summary. + */ + currentErrorOrState_: { + type: String, + computed: 'computeErrorOrStateString_(state.*, ' + + 'settings.copies.valid, settings.scaling.valid, ' + + 'settings.pages.valid, printInProgress_)' + }, + + /** + * @private {{numPages: number, + * numSheets: number, + * pagesLabel: string, + * summaryLabel: string}} + */ + labelInfo_: { + type: Object, + computed: 'getLabelInfo_(currentErrorOrState_, destination.id, ' + + 'settings.copies.value, settings.pages.value, ' + + 'settings.duplex.value)' + }, + }, + + /** @private */ + onPrintButtonTap_: function() { + this.printInProgress_ = true; + }, + + /** @private */ + onCancelButtonTap_: function() { + this.printInProgress_ = false; + }, + + /** + * @return {boolean} + * @private + */ + isPdfOrDrive_: function() { + return this.destination.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF || + this.destination.id == print_preview.Destination.GooglePromotedId.DOCS; + }, + + /** + * @return {string} + * @private + */ + getPrintButton_: function() { + return loadTimeData.getString( + this.isPdfOrDrive_() ? 'saveButton' : 'printButton'); + }, + + /** + * @return {?string} + * @private + */ + computeErrorOrStateString_: function() { + if (this.state.cloudPrintError != '') + return this.state.cloudPrintError; + if (this.state.privetExtensionError != '') + return this.state.privetExtensionError; + if (this.state.invalidSettings || this.state.previewFailed || + this.state.previewLoading || !this.getSetting('copies').valid || + !this.getSetting('scaling').valid || !this.getSetting('pages').valid) { + return ''; + } + if (this.printInProgress_) { + return loadTimeData.getString( + this.isPdfOrDrive_() ? 'saving' : 'printing'); + } + return null; + }, + + /** + * @return {{numPages: number, + * numSheets: number, + * pagesLabel: string, + * summaryLabel: string}} + * @private + */ + getLabelInfo_: function() { + const saveToPdfOrDrive = this.isPdfOrDrive_(); + let numPages = this.getSetting('pages').value.length; + let numSheets = numPages; + if (!saveToPdfOrDrive && this.getSetting('duplex').value) { + numSheets = Math.ceil(numPages / 2); + } + + const copies = /** @type {number} */ (this.getSetting('copies').value); + numSheets *= copies; + numPages *= copies; + + const pagesLabel = loadTimeData.getString('printPreviewPageLabelPlural'); + let summaryLabel; + if (numSheets > 1) { + summaryLabel = saveToPdfOrDrive ? + pagesLabel : + loadTimeData.getString('printPreviewSheetsLabelPlural'); + } else { + summaryLabel = loadTimeData.getString( + saveToPdfOrDrive ? 'printPreviewPageLabelSingular' : + 'printPreviewSheetsLabelSingular'); + } + return { + numPages: numPages, + numSheets: numSheets, + pagesLabel: pagesLabel, + summaryLabel: summaryLabel + }; + }, + + /** + * @return {boolean} + * @private + */ + printButtonDisabled_: function() { + return this.currentErrorOrState_ != null; + }, + + /** + * @return {string} + * @private + */ + getSummary_: function() { + let html = this.currentErrorOrState_; + if (html != null) + return html; + const labelInfo = this.labelInfo_; + if (labelInfo.numPages != labelInfo.numSheets) { + html = loadTimeData.getStringF( + 'printPreviewSummaryFormatLong', + '<b>' + labelInfo.numSheets.toLocaleString() + '</b>', + '<b>' + labelInfo.summaryLabel + '</b>', + labelInfo.numPages.toLocaleString(), labelInfo.pagesLabel); + } else { + html = loadTimeData.getStringF( + 'printPreviewSummaryFormatShort', + '<b>' + labelInfo.numSheets.toLocaleString() + '</b>', + '<b>' + labelInfo.summaryLabel + '</b>'); + } + + // Removing extra spaces from within the string. + html = html.replace(/\s{2,}/g, ' '); + return html; + }, + + /** + * @return {string} + * @private + */ + getSummaryLabel_: function() { + if (this.currentErrorOrState_ != null) + return this.currentErrorOrState_; + const labelInfo = this.labelInfo_; + if (labelInfo.numPages != labelInfo.numSheets) { + return loadTimeData.getStringF( + 'printPreviewSummaryFormatLong', labelInfo.numSheets.toLocaleString(), + labelInfo.summaryLabel, labelInfo.numPages.toLocaleString(), + labelInfo.pagesLabel); + } + return loadTimeData.getStringF( + 'printPreviewSummaryFormatShort', labelInfo.numSheets.toLocaleString(), + labelInfo.summaryLabel); + } +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/input_css.html b/chromium/chrome/browser/resources/print_preview/new/input_css.html new file mode 100644 index 00000000000..e326dc4bfa6 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/input_css.html @@ -0,0 +1,33 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<dom-module id="input"> + <template> + <style> + .input-settings-section { + --error-text-color: rgb(140, 20, 20); + + --invalid-input-style: { + background: rgb(255, 240, 240); + color: var(--error-text-color); + } + } + + input:invalid, + :host .input-settings-section input.invalid { + @apply --invalid-input-style; + } + + span.hint { + background: white; + color: var(--error-text-color); + font-size: 0.9em; + font-weight: bold; + height: auto; + margin-bottom: -5px; + margin-top: 5px; + padding-bottom: 5px; + user-select: text; + } + </style> + </template> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/layout_settings.html b/chromium/chrome/browser/resources/print_preview/new/layout_settings.html new file mode 100644 index 00000000000..d283f42ada5 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/layout_settings.html @@ -0,0 +1,21 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="select_css.html"> +<link rel="import" href="settings_section.html"> + +<dom-module id="print-preview-layout-settings"> + <template> + <style include="print-preview-shared select"></style> + <print-preview-settings-section> + <span id="layout-label" slot="title">$i18n{layoutLabel}</span> + <div slot="controls"> + <select aria-labelledby="layout-label"> + <option value="portrait" selected>$i18n{optionPortrait}</option> + <option value="landscape">$i18n{optionLandscape}</option> + </select> + </div> + </print-preview-settings-section> + </template> + <script src="layout_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/layout_settings.js b/chromium/chrome/browser/resources/print_preview/new/layout_settings.js new file mode 100644 index 00000000000..21bac56700e --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/layout_settings.js @@ -0,0 +1,7 @@ +// 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. + +Polymer({ + is: 'print-preview-layout-settings', +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/margins_settings.html b/chromium/chrome/browser/resources/print_preview/new/margins_settings.html new file mode 100644 index 00000000000..bffdaac1bb1 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/margins_settings.html @@ -0,0 +1,27 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="select_css.html"> +<link rel="import" href="settings_section.html"> + +<dom-module id="print-preview-margins-settings"> + <template> + <style include="print-preview-shared select"> + </style> + <print-preview-settings-section> + <span id="margins-label" slot="title">$i18n{marginsLabel}</span> + <div slot="controls"> + <select aria-labelledby="margins-label"> + <!-- The order of these options must match the natural order of their + values, which come from + print_preview.ticket_items.MarginsTypeValue. --> + <option value="0" selected>$i18n{defaultMargins}</option> + <option value="1">$i18n{noMargins}</option> + <option value="2">$i18n{minimumMargins}</option> + <option value="3">$i18n{customMargins}</option> + </select> + </div> + </print-preview-settings-section> + </template> + <script src="margins_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/margins_settings.js b/chromium/chrome/browser/resources/print_preview/new/margins_settings.js new file mode 100644 index 00000000000..d0e942300ba --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/margins_settings.js @@ -0,0 +1,7 @@ +// 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. + +Polymer({ + is: 'print-preview-margins-settings', +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/media_size_settings.html b/chromium/chrome/browser/resources/print_preview/new/media_size_settings.html new file mode 100644 index 00000000000..a7e74065e51 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/media_size_settings.html @@ -0,0 +1,20 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="select_css.html"> +<link rel="import" href="settings_section.html"> + +<dom-module id="print-preview-media-size-settings"> + <template> + <style include="print-preview-shared select"> + </style> + <print-preview-settings-section> + <span id="media-size-label" slot="title">$i18n{mediaSizeLabel}</span> + <div slot="controls"> + <select aria-labelledby="media-size-label"> + </select> + </div> + </print-preview-settings-section> + </template> + <script src="media_size_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/media_size_settings.js b/chromium/chrome/browser/resources/print_preview/new/media_size_settings.js new file mode 100644 index 00000000000..e425ec73a47 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/media_size_settings.js @@ -0,0 +1,7 @@ +// 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. + +Polymer({ + is: 'print-preview-media-size-settings', +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/model.html b/chromium/chrome/browser/resources/print_preview/new/model.html new file mode 100644 index 00000000000..3c40ff26599 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/model.html @@ -0,0 +1,6 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="../data/destination.html"> +<link rel="import" href="../data/document_info.html"> + +<script src="model.js"></script> diff --git a/chromium/chrome/browser/resources/print_preview/new/model.js b/chromium/chrome/browser/resources/print_preview/new/model.js new file mode 100644 index 00000000000..2925555aaa0 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/model.js @@ -0,0 +1,338 @@ +// 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.exportPath('print_preview_new'); + +/** + * @typedef {{ + * value: *, + * valid: boolean, + * available: boolean, + * updatesPreview: boolean + * }} + */ +print_preview_new.Setting; + +/** + * @typedef {{ + * previewLoading: boolean, + * previewFailed: boolean, + * cloudPrintError: string, + * privetExtensionError: string, + * invalidSettings: boolean, + * }} + */ +print_preview_new.State; + +Polymer({ + is: 'print-preview-model', + + properties: { + /** + * Object containing current settings of Print Preview, for use by Polymer + * controls. + * @type {{ + * pages: !print_preview_new.Setting, + * copies: !print_preview_new.Setting, + * collate: !print_preview_new.Setting, + * layout: !print_preview_new.Setting, + * color: !print_preview_new.Setting, + * mediaSize: !print_preview_new.Setting, + * margins: !print_preview_new.Setting, + * dpi: !print_preview_new.Setting, + * fitToPage: !print_preview_new.Setting, + * scaling: !print_preview_new.Setting, + * duplex: !print_preview_new.Setting, + * cssBackground: !print_preview_new.Setting, + * selectionOnly: !print_preview_new.Setting, + * headerFooter: !print_preview_new.Setting, + * rasterize: !print_preview_new.Setting, + * vendorItems: !print_preview_new.Setting, + * }} + */ + settings: { + type: Object, + notify: true, + value: { + pages: { + value: [1, 2, 3, 4, 5], + valid: true, + available: true, + updatesPreview: true, + }, + copies: { + value: '1', + valid: true, + available: true, + updatesPreview: false, + }, + collate: { + value: true, + valid: true, + available: true, + updatesPreview: false, + }, + layout: { + value: false, /* portrait */ + valid: true, + available: true, + updatesPreview: true, + }, + color: { + value: true, /* color */ + valid: true, + available: true, + updatesPreview: true, + }, + mediaSize: { + value: { + width_microns: 215900, + height_microns: 279400, + }, + valid: true, + available: true, + updatesPreview: true, + }, + margins: { + value: 0, + valid: true, + available: true, + updatesPreview: true, + }, + dpi: { + value: {}, + valid: true, + available: true, + updatesPreview: false, + }, + fitToPage: { + value: false, + valid: true, + available: true, + updatesPreview: true, + }, + scaling: { + value: '100', + valid: true, + available: true, + updatesPreview: true, + }, + duplex: { + value: true, + valid: true, + available: true, + updatesPreview: false, + }, + cssBackground: { + value: false, + valid: true, + available: true, + updatesPreview: true, + }, + selectionOnly: { + value: false, + valid: true, + available: true, + updatesPreview: true, + }, + headerFooter: { + value: true, + valid: true, + available: true, + updatesPreview: true, + }, + rasterize: { + value: false, + valid: true, + available: true, + updatesPreview: false, + }, + vendorItems: { + value: {}, + valid: true, + available: true, + updatesPreview: false, + }, + }, + }, + + /** @type {print_preview.Destination} */ + destination: { + type: Object, + notify: true, + value: function() { + const dest = new print_preview.Destination( + 'Foo Printer', print_preview.DestinationType.LOCAL, + print_preview.DestinationOrigin.LOCAL, 'Foo Printer', true, + print_preview.DestinationConnectionStatus.ONLINE, + {description: 'PrinterBrandAA 12345'}); + dest.capabilities = { + version: '1.0', + printer: { + collate: {default: true}, + color: { + option: [ + {type: 'STANDARD_COLOR', is_default: true}, + {type: 'STANDARD_MONOCHROME'} + ] + }, + copies: {default: 1, max: 1000}, + dpi: { + option: [ + {horizontal_dpi: 200, vertical_dpi: 200, is_default: true}, + {horizontal_dpi: 100, vertical_dpi: 100}, + ] + }, + duplex: { + option: [ + {type: 'NO_DUPLEX', is_default: true}, {type: 'LONG_EDGE'}, + {type: 'SHORT_EDGE'} + ] + }, + page_orientation: { + option: [ + {type: 'PORTRAIT', is_default: true}, {type: 'LANDSCAPE'}, + {type: 'AUTO'} + ] + }, + media_size: { + option: [ + { + name: 'NA_LETTER', + width_microns: 215900, + height_microns: 279400, + is_default: true, + custom_display_name: 'Letter', + }, + { + name: 'CUSTOM_SQUARE', + width_microns: 215900, + height_microns: 215900, + custom_display_name: 'CUSTOM_SQUARE', + } + ] + }, + vendor_capability: [], + } + }; + return dest; + }, + }, + + /** @type {print_preview.DocumentInfo} */ + documentInfo: { + type: Object, + notify: true, + value: function() { + const info = new print_preview.DocumentInfo(); + info.init(false, 'DocumentTitle', true); + info.updatePageCount(5); + info.fitToPageScaling_ = 94; + return info; + }, + }, + + /** @type {!print_preview_new.State} */ + state: { + type: Object, + notify: true, + value: { + previewLoading: false, + previewFailed: false, + cloudPrintError: '', + privetExtensionError: '', + invalidSettings: false, + }, + }, + }, + + observers: + ['updateSettingsAvailable_(' + + 'destination.id, destination.capabilities, ' + + 'documentInfo.isModifiable, documentInfo.hasCssMediaStyles,' + + 'documentInfo.hasSelection)'], + /** + * @private {!Array<string>} List of capability types considered color. + * @const + */ + COLOR_TYPES_: ['STANDARD_COLOR', 'CUSTOM_COLOR'], + + /** + * @private {!Array<string>} List of capability types considered monochrome. + * @const + */ + MONOCHROME_TYPES_: ['STANDARD_MONOCHROME', 'CUSTOM_MONOCHROME'], + + /** + * Updates the availability of the settings sections. + * @private + */ + updateSettingsAvailable_: function() { + const caps = (!!this.destination && !!this.destination.capabilities) ? + this.destination.capabilities.printer : + null; + const isSaveToPdf = this.destination.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF; + const knownSizeToSaveAsPdf = isSaveToPdf && + (!this.documentInfo.isModifiable || + this.documentInfo.hasCssMediaStyles); + + this.set('settings.copies.available', !!caps && !!(caps.copies)); + this.set('settings.collate.available', !!caps && !!(caps.collate)); + this.set('settings.layout.available', this.isLayoutAvailable_(caps)); + this.set('settings.color.available', this.isColorAvailable_(caps)); + this.set('settings.margins.available', this.documentInfo.isModifiable); + this.set( + 'settings.mediaSize.available', + !!caps && !!caps.media_size && !knownSizeToSaveAsPdf); + this.set( + 'settings.dpi.available', + !!caps && !!caps.dpi && !!caps.dpi.option && + caps.dpi.option.length > 1); + this.set( + 'settings.fitToPage.available', + !this.documentInfo.isModifiable && !isSaveToPdf); + this.set('settings.scaling.available', !knownSizeToSaveAsPdf); + this.set('settings.duplex.available', !!caps && !!caps.duplex); + this.set( + 'settings.cssBackground.available', this.documentInfo.isModifiable); + this.set( + 'settings.selectionOnly.available', + this.documentInfo.isModifiable && this.documentInfo.hasSelection); + this.set('settings.headerFooter.available', this.documentInfo.isModifiable); + this.set('settings.rasterize.available', !this.documentInfo.isModifiable); + }, + + /** @param {?print_preview.CddCapabilities} caps The printer capabilities. */ + isLayoutAvailable_: function(caps) { + if (!caps || !caps.page_orientation || !caps.page_orientation.option || + !this.documentInfo.isModifiable || + this.documentInfo.hasCssMediaStyles) { + return false; + } + let hasAutoOrPortraitOption = false; + let hasLandscapeOption = false; + caps.page_orientation.option.forEach(option => { + hasAutoOrPortraitOption = hasAutoOrPortraitOption || + option.type == 'AUTO' || option.type == 'PORTRAIT'; + hasLandscapeOption = hasLandscapeOption || option.type == 'LANDSCAPE'; + }); + return hasLandscapeOption && hasAutoOrPortraitOption; + }, + + /** @param {?print_preview.CddCapabilities} caps The printer capabilities. */ + isColorAvailable_: function(caps) { + if (!caps || !caps.color || !caps.color.option) + return false; + let hasColor = false; + let hasMonochrome = false; + caps.color.option.forEach(option => { + const type = assert(option.type); + hasColor = hasColor || this.COLOR_TYPES_.includes(option.type); + hasMonochrome = + hasMonochrome || this.MONOCHROME_TYPES_.includes(option.type); + }); + return hasColor && hasMonochrome; + }, +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/number_settings_section.html b/chromium/chrome/browser/resources/print_preview/new/number_settings_section.html new file mode 100644 index 00000000000..dfea6be0f89 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/number_settings_section.html @@ -0,0 +1,48 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="input_css.html"> +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="settings_section.html"> + +<dom-module id="print-preview-number-settings-section"> + <template> + <style include="print-preview-shared input"> + input[type='number'] { + -webkit-margin-end: 16px; + margin-bottom: 2.5px; + margin-top: 2.5px; + } + + :host(.multirow-controls) [slot='title'] { + --settings-section-title-style: { + padding-top: 7px; + align-self: flex-start; + } + } + + :host .input-wrapper { + align-items: center; + display: flex; + width: 100%; + } + </style> + <print-preview-settings-section + class="input-settings-section multirow-controls"> + <span slot="title" id="section-title">[[inputLabel]]</span> + <div slot="controls"> + <slot name="opt-outside-content"></slot> + <span class="input-wrapper"> + <input class="user-value" type="number" value="{{inputString::input}}" + max="[[maxValue]]" min="[[minValue]]" on-blur="onBlur_" + on-keydown="onKeydown_" aria-labelled-by="section-title"> + <slot name="opt-inside-content"></slot> + </span> + <span class="hint" aria-live="polite" + hidden$="[[hintHidden_(inputString, inputValid)]]"> + [[hintMessage]] + </span> + </div> + </print-preview-settings-section> + </template> + <script src="number_settings_section.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/number_settings_section.js b/chromium/chrome/browser/resources/print_preview/new/number_settings_section.js new file mode 100644 index 00000000000..4084b852b71 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/number_settings_section.js @@ -0,0 +1,70 @@ +// 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. + +Polymer({ + is: 'print-preview-number-settings-section', + + properties: { + /** @type {string} */ + inputString: { + type: String, + notify: true, + }, + + /** @type {boolean} */ + inputValid: { + type: Boolean, + notify: true, + computed: 'computeValid_(inputString)', + }, + + /** @type {string} */ + defaultValue: String, + + /** @type {number} */ + maxValue: Number, + + /** @type {number} */ + minValue: Number, + + /** @type {string} */ + inputLabel: String, + + /** @type {string} */ + hintMessage: String, + }, + + /** + * @param {!KeyboardEvent} e The keyboard event + */ + onKeydown_: function(e) { + if (e.key == '.' || e.key == 'e' || e.key == '-') + e.preventDefault(); + }, + + /** @private */ + onBlur_: function() { + if (this.inputString == '') + this.set('inputString', this.defaultValue); + }, + + /** + * @return {boolean} Whether input value represented by inputString is + * valid. + * @private + */ + computeValid_: function() { + // Make sure value updates first, in case inputString was updated by JS. + this.$$('.user-value').value = this.inputString; + return this.$$('.user-value').validity.valid && this.inputString != ''; + }, + + /** + * @return {boolean} Whether error message should be hidden. + * @private + */ + hintHidden_: function() { + return this.inputValid || this.inputString == ''; + }, +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/other_options_settings.html b/chromium/chrome/browser/resources/print_preview/new/other_options_settings.html new file mode 100644 index 00000000000..71349530d43 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/other_options_settings.html @@ -0,0 +1,48 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="checkbox_radio_css.html"> +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="settings_section.html"> + +<dom-module id="print-preview-other-options-settings"> + <template> + <style include="print-preview-shared checkbox-radio"> + </style> + <print-preview-settings-section class="multirow-controls"> + <span slot="title" id="options-label">$i18n{optionsLabel}</span> + <div slot="controls" class="checkbox"> + <div id="header-footer-container"> + <label aria-live="polite"> + <input type="checkbox"> + <span>$i18n{optionHeaderFooter}</span> + </label> + </div> + <div id="duplex-container"> + <label aria-live="polite"> + <input type="checkbox" checked="{{duplexValue_::change}}"> + <span>$i18n{optionTwoSided}</span> + </label> + </div> + <div id="css-background-container"> + <label aria-live="polite"> + <input type="checkbox"> + <span>$i18n{optionBackgroundColorsAndImages}</span> + </label> + </div> + <div id="rasterize-container"> + <label aria-live="polite"> + <input type="checkbox"> + <span>$i18n{optionRasterize}</span> + </label> + </div> + <div id="selection-only-container"> + <label aria-live="polite"> + <input type="checkbox"> + <span>$i18n{optionSelectionOnly}</span> + </label> + </div> + </div> + </print-preview-settings-section> + </template> + <script src="other_options_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/other_options_settings.js b/chromium/chrome/browser/resources/print_preview/new/other_options_settings.js new file mode 100644 index 00000000000..5399d54526d --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/other_options_settings.js @@ -0,0 +1,32 @@ +// 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. + +Polymer({ + is: 'print-preview-other-options-settings', + + behaviors: [SettingsBehavior], + + properties: { + /** @private {boolean} */ + duplexValue_: Boolean, + }, + + observers: [ + 'onInitialized_(settings.duplex.value)', + 'onDuplexChange_(duplexValue_)', + ], + + isInitialized_: false, + + onInitialized_: function() { + if (this.isInitialized_) + return; + this.set('duplexValue_', this.getSetting('duplex').value); + this.isInitialized_ = true; + }, + + onDuplexChange_: function() { + this.setSetting('duplex', this.duplexValue_); + }, +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/pages_settings.html b/chromium/chrome/browser/resources/print_preview/new/pages_settings.html new file mode 100644 index 00000000000..72fc32d2160 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/pages_settings.html @@ -0,0 +1,58 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="checkbox_radio_css.html"> +<link rel="import" href="../data/document_info.html"> +<link rel="import" href="input_css.html"> +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="settings_behavior.html"> +<link rel="import" href="settings_section.html"> + +<dom-module id="print-preview-pages-settings"> + <template> + <style include="print-preview-shared checkbox-radio input"> + :host .radio .user-value { + -webkit-margin-start: 0.6em; + flex: 1; + width: 100%; + } + + [slot='title'] { + --settings-section-title-style: { + padding-top: 7px; + align-self: flex-start; + } + } + </style> + <print-preview-settings-section + class="input-settings-section multirow-controls"> + <span slot="title">$i18n{pagesLabel}</span> + <div slot="controls"> + <div class="radio"> + <label><input type="radio" name="pages" id="all-radio-button" + checked="{{allSelected_::change}}"> + <span>$i18n{optionAllPages}</span> + </label> + <label class="custom-input-wrapper" + for="page-settings-custom-input" tabindex=-1> + <input type="radio" name="pages" id="custom-radio-button" + on-click="onCustomRadioClick_"> + <input class="user-value" type="text" + value="{{inputString_::input}}" id="page-settings-custom-input" + checked="{{customSelected_::change}}" + pattern="([0-9]*(-)?[0-9]*(,)( )?)*([0-9]*(-)?[0-9]*(,)?( )?)?" + on-focus="onCustomInputFocus_" on-blur="onCustomInputBlur_" + placeholder="$i18n{examplePageRangeText}" + aria-label="$i18n{examplePageRangeText}"> + </label> + </div> + <span class="hint" aria-live="polite" + inner-h-t-m-l="[[getHintMessage_(errorState_, + documentInfo.pageCount)]]" + hidden$="[[hintHidden_(inputString_, errorState_)]]"> + </span> + </div> + </print-preview-settings-section> + </template> + <script src="pages_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/pages_settings.js b/chromium/chrome/browser/resources/print_preview/new/pages_settings.js new file mode 100644 index 00000000000..7b1408b6ac2 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/pages_settings.js @@ -0,0 +1,215 @@ +// 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.exportPath('print_preview_new'); + +/** @enum {number} */ +const PagesInputErrorState = { + NO_ERROR: 0, + INVALID_SYNTAX: 1, + OUT_OF_BOUNDS: 2, +}; + +Polymer({ + is: 'print-preview-pages-settings', + + behaviors: [SettingsBehavior], + + properties: { + /** @type {!print_preview.DocumentInfo} */ + documentInfo: Object, + + /** @private {string} */ + inputString_: { + type: String, + value: '', + }, + + /** @private {!Array<number>} */ + allPagesArray_: { + type: Array, + computed: 'computeAllPagesArray_(documentInfo.pageCount)', + }, + + /** @private {boolean} */ + allSelected_: { + type: Boolean, + value: true, + }, + + /** @private {boolean} */ + customSelected_: { + type: Boolean, + value: false, + }, + + /** @private {!Array<number>} */ + pagesToPrint_: { + type: Array, + computed: 'computePagesToPrint_(' + + 'inputString_, allSelected_, allPagesArray_)', + }, + + /** @private {!PagesInputErrorState} */ + errorState_: { + type: Number, + computed: 'computeErrorState_(pagesToPrint_)', + }, + + }, + + observers: [ + 'onRangeChange_(errorState_, pagesToPrint_)', + 'onRadioChange_(allSelected_, customSelected_)' + ], + + /** + * @return {!Array<number>} + * @private + */ + computeAllPagesArray_: function() { + const array = new Array(this.documentInfo.pageCount); + for (let i = 0; i < array.length; i++) + array[i] = i + 1; + return array; + }, + + /** + * Updates pages to print and error state based on the validity and + * current value of the input. + * @return {!Array<number>} + * @private + */ + computePagesToPrint_: function() { + if (this.allSelected_ || this.inputString_.trim() == '') + return this.allPagesArray_; + if (!this.$$('.user-value').validity.valid) + return []; + + const pages = []; + const added = {}; + const ranges = this.inputString_.split(','); + const maxPage = this.allPagesArray_.length; + for (let range of ranges) { + range = range.trim(); + if (range == '') + continue; + const limits = range.split('-'); + let min = parseInt(limits[0], 10); + if (min < 1) + return []; + if (limits.length == 1) { + if (min > maxPage) + return [-1]; + if (!added.hasOwnProperty(min)) { + pages.push(min); + added[min] = true; + } + continue; + } + + let max = parseInt(limits[1], 10); + if (isNaN(min)) + min = 1; + if (isNaN(max)) + max = maxPage; + if (min > max) + return []; + if (max > maxPage) + return [-1]; + for (let i = min; i <= max; i++) { + if (!added.hasOwnProperty(i)) { + pages.push(i); + added[i] = true; + } + } + } + return pages; + }, + + /** + * @return {!PagesInputErrorState} + * @private + */ + computeErrorState_: function() { + if (this.pagesToPrint_.length == 0) + return PagesInputErrorState.INVALID_SYNTAX; + if (this.pagesToPrint_[0] == -1) + return PagesInputErrorState.OUT_OF_BOUNDS; + return PagesInputErrorState.NO_ERROR; + }, + + /** + * Updates the model with pages and validity, and adds error styling if + * needed. + * @private + */ + onRangeChange_: function() { + if (this.errorState_ != PagesInputErrorState.NO_ERROR) { + this.setSettingValid('pages', false); + this.$$('.user-value').classList.add('invalid'); + return; + } + this.$$('.user-value').classList.remove('invalid'); + this.setSettingValid('pages', true); + this.setSetting('pages', this.pagesToPrint_); + }, + + /** @private */ + onRadioChange_: function() { + if (this.$$('#all-radio-button').checked) + this.customSelected_ = false; + if (this.$$('#custom-radio-button').checked) + this.allSelected_ = false; + }, + + /** @private */ + onCustomRadioClick_: function() { + this.$$('#page-settings-custom-input').focus(); + }, + + /** @private */ + onCustomInputFocus_: function() { + this.$$('#all-radio-button').checked = false; + this.$$('#custom-radio-button').checked = true; + this.customSelected_ = true; + }, + + /** + * @param {Event} event Contains information about where focus is going. + * @private + */ + onCustomInputBlur_: function(event) { + if (this.inputString_.trim() == '' && + event.relatedTarget != this.$$('.custom-input-wrapper') && + event.relatedTarget != this.$$('#custom-radio-button')) { + this.$$('#all-radio-button').checked = true; + this.$$('#custom-radio-button').checked = false; + this.allSelected_ = true; + } + }, + + /** + * @return {string} Gets message to show as hint. + * @private + */ + getHintMessage_: function() { + if (this.errorState_ == PagesInputErrorState.INVALID_SYNTAX) { + return loadTimeData.getStringF( + 'pageRangeSyntaxInstruction', + loadTimeData.getString('examplePageRangeText')); + } else { + return loadTimeData.getStringF( + 'pageRangeLimitInstructionWithValue', this.documentInfo.pageCount); + } + }, + + /** + * @return {boolean} Whether to hide the hint. + * @private + */ + hintHidden_: function() { + return this.errorState_ == PagesInputErrorState.NO_ERROR; + } +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/print_preview_shared_css.html b/chromium/chrome/browser/resources/print_preview/new/print_preview_shared_css.html new file mode 100644 index 00000000000..63bcc781674 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/print_preview_shared_css.html @@ -0,0 +1,136 @@ +<!-- Most styles copied from resources/css/widgets.css. --> +<link rel="import" href="chrome://resources/html/polymer.html"> + +<dom-module id="print-preview-shared"> + <template> + <style> + h1 { + line-height: 1; + user-select: none; + } + + /* Default state ********************************************************/ + + button, + input[type='button'], + select, + input[type='checkbox'], + input[type='radio'] { + -webkit-appearance: none; + background-image: linear-gradient(#ededed, #ededed 38%, #dedede); + border: 1px solid rgba(0, 0, 0, 0.25); + border-radius: 2px; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08), + inset 0 1px 2px rgba(255, 255, 255, 0.75); + color: #444; + font: inherit; + margin: 0 1px 0 0; + outline: none; + text-shadow: 0 1px 0 rgb(240, 240, 240); + user-select: none; + } + + button, + input[type='button'], + select { + min-height: 2em; + min-width: 4em; + padding-bottom: 1px; + <if expr="is_win or is_macosx"> + /* The following platform-specific rule is necessary to get adjacent + * buttons, text inputs, and so forth to align on their borders while + * also aligning on the text's baselines. */ + padding-bottom: 2px; + </if> + padding-top: 1px; + } + + input[type='number'], + input[type='search'], + input[type='text'], + input:not([type]), + textarea { + border: 1px solid #bfbfbf; + border-radius: 2px; + box-sizing: border-box; + color: #444; + font: inherit; + margin: 0; + /* Use min-height to accommodate addditional padding for touch as + * needed. */ + min-height: 2em; + outline: none; + padding: 3px; + <if expr="is_win or is_macosx or is_ios"> + /* For better alignment between adjacent buttons and inputs. */ + padding-bottom: 4px; + </if> + } + + :root { + <if expr="not is_ios"> + /* Hover **************************************************************/ + --print-preview-hover: { + border-color: rgba(0, 0, 0, 0.3); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.12), + inset 0 1px 2px rgba(255, 255, 255, 0.95); + color: black; + }; + </if> + + /* Active *************************************************************/ + --print-preview-active: { + box-shadow: none; + text-shadow: none; + }; + } + + /* Disabled *************************************************************/ + + :disabled:-webkit-any( + button, + input[type='button'], + select) { + background-image: linear-gradient(#f1f1f1, #f1f1f1 38%, #e6e6e6); + border-color: rgba(80, 80, 80, 0.2); + box-shadow: 0 1px 0 rgba(80, 80, 80, 0.08), + inset 0 1px 2px rgba(255, 255, 255, 0.75); + color: #aaa; + } + + input:disabled:-webkit-any([type='search'], + [type='text'], + :not([type])) { + color: #999; + } + + /* Focus ****************************************************************/ + + :enabled:focus:-webkit-any( + select, + input[type='checkbox'], + input[type='number'], + input[type='radio'], + input[type='search'], + input[type='text'], + input:not([type]), + button, + input[type='button']) { + /* We use border color because it follows the border radius (unlike + * outline). This is particularly noticeable on mac. */ + border-color: rgb(77, 144, 254); + outline: none; + /* OVERRIDE */ + transition: border-color 200ms; + } + + html:not(.focus-outline-visible) + :enabled:focus:-webkit-any(input[type='checkbox'], + input[type='radio'], + button) { + border-color: rgba(0,0,0,0.25); + } + + </style> + </template> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/scaling_settings.html b/chromium/chrome/browser/resources/print_preview/new/scaling_settings.html new file mode 100644 index 00000000000..6386cd27cab --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/scaling_settings.html @@ -0,0 +1,28 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="checkbox_radio_css.html"> +<link rel="import" href="../data/document_info.html"> +<link rel="import" href="number_settings_section.html"> +<link rel="import" href="print_preview_shared_css.html"> +<link rel="import" href="settings_behavior.html"> + +<dom-module id="print-preview-scaling-settings"> + <template> + <style include="print-preview-shared checkbox-radio"> + </style> + <print-preview-number-settings-section max-value=200 min-value=10 + default-value="100" input-label="$i18n{scalingLabel}" + input-string="{{inputString_}}" input-valid="{{inputValid_}}" + hint-message="$i18n{scalingInstruction}" class="multirow-controls"> + <div slot="opt-outside-content" id="fit-to-page-container" + class="checkbox" hidden$="[[!settings.fitToPage.available]]"> + <label aria-live="polite"> + <input class="checkbox" type="checkbox" + on-change="onFitToPageChange_"> + <span>$i18n{optionFitToPage}</span> + </label> + </div> + </print-preview-number-settings-section> + </template> + <script src="scaling_settings.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/scaling_settings.js b/chromium/chrome/browser/resources/print_preview/new/scaling_settings.js new file mode 100644 index 00000000000..a6495e85baf --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/scaling_settings.js @@ -0,0 +1,80 @@ +// 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. + +Polymer({ + is: 'print-preview-scaling-settings', + + behaviors: [SettingsBehavior], + + properties: { + /** @type {Object} */ + documentInfo: Object, + + /** @private {string} */ + inputString_: String, + + /** @private {boolean} */ + inputValid_: Boolean, + }, + + /** @private {string} */ + lastValidScaling_: '100', + + /** @private {number} */ + fitToPageFlag_: 0, + + /** @private {boolean} */ + isInitialized_: false, + + observers: [ + 'onInputChanged_(inputString_, inputValid_, documentInfo.isModifiable)', + 'onInitialized_(settings.scaling.value)' + ], + + /** + * Updates the input string when the setting has been initialized. + * @private + */ + onInitialized_: function() { + // Avoid loops from setting inputString_ -> onInputChanged_ sets scaling + // value -> onInitialized_ sets inputString_ + if (this.isInitialized_) + return; + this.isInitialized_ = true; + const scaling = this.getSetting('scaling'); + this.inputString_ = /** @type {string} */ (scaling.value); + }, + + /** + * Updates model.settings.scaling based on the validity and current value of + * the scaling input. + * @private + */ + onInputChanged_: function() { + if (this.fitToPageFlag_ > 0) { + this.fitToPageFlag_--; + } else { + const checkbox = this.$$('.checkbox input[type="checkbox"]'); + if (checkbox.checked && !this.documentInfo.isModifiable) { + checkbox.checked = false; + } else if (this.inputValid_) { + this.lastValidScaling_ = this.inputString_; + } + this.setSetting('scaling', this.inputString_); + } + this.setSettingValid('scaling', this.inputValid_); + }, + + /** + * Updates scaling as needed based on the value of the fit to page checkbox. + */ + onFitToPageChange_: function() { + if (this.$$('.checkbox input[type="checkbox"]').checked) { + this.fitToPageFlag_ = 2; + this.set('inputString_', this.documentInfo.fitToPageScaling); + } else { + this.set('inputString_', this.lastValidScaling_); + } + }, +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/select_css.html b/chromium/chrome/browser/resources/print_preview/new/select_css.html new file mode 100644 index 00000000000..5a316158da1 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/select_css.html @@ -0,0 +1,40 @@ +<!-- Most styles copied from resources/css/widgets.css. --> +<link rel="import" href="chrome://resources/html/polymer.html"> + +<dom-module id="select"> + <template> + <style include="print-preview-shared"> + select { + -webkit-appearance: none; + -webkit-padding-end: 24px; + -webkit-padding-start: 10px; + /* OVERRIDE */ + background-image: url(chrome://resources/images/select.png), + linear-gradient(#ededed, #ededed 38%, #dedede); + background-position: right center; + background-repeat: no-repeat; + height: 28px; + width: 100%; + } + + <if expr="not is_ios"> + select:enabled:hover { + background-image: url(chrome://resources/images/select.png), + linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0); + @apply(--print-preview-hover); + } + </if> + + select:enabled:active { + background-image: url(chrome://resources/images/select.png), + linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7); + @apply(--print-preview-active); + } + + select:disabled { + background-image: url(chrome://resources/images/disabled_select.png), + linear-gradient(#f1f1f1, #f1f1f1 38%, #e6e6e6); + } + </style> + </template> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/settings_behavior.html b/chromium/chrome/browser/resources/print_preview/new/settings_behavior.html new file mode 100644 index 00000000000..72cf371c35d --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/settings_behavior.html @@ -0,0 +1,3 @@ +<link rel="import" href="chrome://resources/html/assert.html"> + +<script src="settings_behavior.js"></script> diff --git a/chromium/chrome/browser/resources/print_preview/new/settings_behavior.js b/chromium/chrome/browser/resources/print_preview/new/settings_behavior.js new file mode 100644 index 00000000000..9d295229086 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/settings_behavior.js @@ -0,0 +1,48 @@ +// 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. + +/** @polymerBehavior */ +const SettingsBehavior = { + properties: { + /** @type {Object} */ + settings: { + type: Object, + notify: true, + }, + }, + + /** + * @param {string} settingName Name of the setting to get. + * @return {print_preview_new.Setting} The setting object. + */ + getSetting: function(settingName) { + const setting = /** @type {print_preview_new.Setting} */ ( + this.get(settingName, this.settings)); + assert(!!setting, 'Setting is missing: ' + settingName); + return setting; + }, + + /** + * @param {string} settingName Name of the setting to set + * @param {boolean | string | number | Array | Object} value The value to set + * the setting to. + */ + setSetting: function(settingName, value) { + const setting = this.getSetting(settingName); + this.set(`settings.${settingName}.value`, value); + }, + + /** + * @param {string} settingName Name of the setting to set + * @param {boolean} valid Whether the setting value is currently valid. + */ + setSettingValid: function(settingName, valid) { + const setting = this.getSetting(settingName); + // Should not set the setting to invalid if it is not available, as there + // is no way for the user to change the value in this case. + if (!valid) + assert(setting.available, 'Setting is not available: ' + settingName); + this.set(`settings.${settingName}.valid`, valid); + } +}; diff --git a/chromium/chrome/browser/resources/print_preview/new/settings_section.html b/chromium/chrome/browser/resources/print_preview/new/settings_section.html new file mode 100644 index 00000000000..be94c427342 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/settings_section.html @@ -0,0 +1,34 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<dom-module id="print-preview-settings-section"> + <template> + <style> + :host { + align-items: center; + display: flex; + margin-top: 10px; + padding: 5px 20px; + } + + ::slotted([slot=controls]) { + flex: 1; + } + + ::slotted([slot=title]) { + -webkit-padding-end: 20px; + color: #646464; + font-size: 1em; + width: 70px; + word-break: break-word; + } + + :host(.multirow-controls) ::slotted([slot=title]) { + align-self: flex-start; + padding-top: 7px; + } + </style> + <slot name="title"></slot> + <slot name="controls"></slot> + </template> + <script src="settings_section.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/new/settings_section.js b/chromium/chrome/browser/resources/print_preview/new/settings_section.js new file mode 100644 index 00000000000..a28a5054954 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/settings_section.js @@ -0,0 +1,7 @@ +// 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. + +Polymer({ + is: 'print-preview-settings-section', +}); diff --git a/chromium/chrome/browser/resources/print_preview/new/strings.html b/chromium/chrome/browser/resources/print_preview/new/strings.html new file mode 100644 index 00000000000..3bf66307de1 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/strings.html @@ -0,0 +1,2 @@ +<link rel="import" href="chrome://resources/html/load_time_data.html"> +<script src="../strings.js"></script> diff --git a/chromium/chrome/browser/resources/print_preview/new/throbber_css.html b/chromium/chrome/browser/resources/print_preview/new/throbber_css.html new file mode 100644 index 00000000000..747c1667f65 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/new/throbber_css.html @@ -0,0 +1,15 @@ +<!-- Styles copied from resources/css/throbber.css. --> +<link rel="import" href="chrome://resources/html/polymer.html"> + +<dom-module id="throbber"> + <template> + <style> + .throbber { + background: url(chrome://resources/images/throbber_small.svg) no-repeat; + display: inline-block; + height: 16px; + width: 16px; + } + </style> + </template> +</dom-module> diff --git a/chromium/chrome/browser/resources/print_preview/preview_generator.js b/chromium/chrome/browser/resources/print_preview/preview_generator.js index 8d47ae1275b..93c0c2f083c 100644 --- a/chromium/chrome/browser/resources/print_preview/preview_generator.js +++ b/chromium/chrome/browser/resources/print_preview/preview_generator.js @@ -5,166 +5,133 @@ cr.define('print_preview', function() { 'use strict'; - /** - * Interface to the Chromium print preview generator. - * @param {!print_preview.DestinationStore} destinationStore Used to get the - * currently selected destination. - * @param {!print_preview.PrintTicketStore} printTicketStore Used to read the - * state of the ticket and write document information. - * @param {!print_preview.NativeLayer} nativeLayer Used to communicate to - * Chromium's preview rendering system. - * @param {!print_preview.DocumentInfo} documentInfo Document data model. - * @param {!WebUIListenerTracker} listenerTracker Tracker for the WebUI - * listeners added in the PreviewGenerator constructor. - * @constructor - * @extends {cr.EventTarget} - */ - function PreviewGenerator( - destinationStore, printTicketStore, nativeLayer, documentInfo, - listenerTracker) { - cr.EventTarget.call(this); - - /** - * Used to get the currently selected destination. - * @type {!print_preview.DestinationStore} - * @private - */ - this.destinationStore_ = destinationStore; - - /** - * Used to read the state of the ticket and write document information. - * @type {!print_preview.PrintTicketStore} - * @private - */ - this.printTicketStore_ = printTicketStore; - - /** - * Interface to the Chromium native layer. - * @type {!print_preview.NativeLayer} - * @private - */ - this.nativeLayer_ = nativeLayer; - - /** - * Document data model. - * @type {!print_preview.DocumentInfo} - * @private - */ - this.documentInfo_ = documentInfo; - - /** - * ID of current in-flight request. Requests that do not share this ID will - * be ignored. - * @type {number} - * @private - */ - this.inFlightRequestId_ = -1; - - /** - * Whether the current in flight request requires generating draft pages for - * print preview. This is true only for modifiable documents when the print - * settings has changed sufficiently to require re-rendering. - * @private {boolean} - */ - this.generateDraft_ = false; - - /** - * Media size to generate preview with. {@code null} indicates default size. - * @type {print_preview.ValueType} - * @private - */ - this.mediaSize_ = null; - + class PreviewGenerator extends cr.EventTarget { /** - * Whether the previews are being generated in landscape mode. - * @type {boolean} - * @private - */ - this.isLandscapeEnabled_ = false; - - /** - * Whether the previews are being generated with a header and footer. - * @type {boolean} - * @private - */ - this.isHeaderFooterEnabled_ = false; - - /** - * Whether the previews are being generated in color. - * @type {boolean} - * @private - */ - this.colorValue_ = false; - - /** - * Whether the document should be fitted to the page. - * @type {boolean} - * @private - */ - this.isFitToPageEnabled_ = false; - - /** - * The scaling factor (in percent) for the document. Ignored if fit to page - * is true. - * @type {number} - * @private - */ - this.scalingValue_ = 100; - - /** - * Page ranges setting used used to generate the last preview. - * @type {Array<{from: number, to: number}>} - * @private + * Interface to the Chromium print preview generator. + * @param {!print_preview.DestinationStore} destinationStore Used to get the + * currently selected destination. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to read + * the state of the ticket and write document information. + * @param {!print_preview.NativeLayer} nativeLayer Used to communicate to + * Chromium's preview rendering system. + * @param {!print_preview.DocumentInfo} documentInfo Document data model. + * @param {!WebUIListenerTracker} listenerTracker Tracker for the WebUI + * listeners added in the PreviewGenerator constructor. */ - this.pageRanges_ = null; - - /** - * Margins type used to generate the last preview. - * @type {!print_preview.ticket_items.MarginsTypeValue} - * @private - */ - this.marginsType_ = print_preview.ticket_items.MarginsTypeValue.DEFAULT; - - /** - * Whether the document should have element CSS backgrounds printed. - * @type {boolean} - * @private - */ - this.isCssBackgroundEnabled_ = false; - - /** - * Destination that was selected for the last preview. - * @type {print_preview.Destination} - * @private - */ - this.selectedDestination_ = null; - - this.addWebUIEventListeners_(listenerTracker); - } - - /** - * Event types dispatched by the preview generator. - * @enum {string} - */ - PreviewGenerator.EventType = { - // Dispatched when the document can be printed. - DOCUMENT_READY: 'print_preview.PreviewGenerator.DOCUMENT_READY', - - // Dispatched when a page preview is ready. The previewIndex field of the - // event is the index of the page in the modified document, not the - // original. So page 4 of the original document might be previewIndex = 0 of - // the modified document. - PAGE_READY: 'print_preview.PreviewGenerator.PAGE_READY', - - // Dispatched when the document preview starts to be generated. - PREVIEW_START: 'print_preview.PreviewGenerator.PREVIEW_START', - - // Dispatched when the current print preview request fails. - FAIL: 'print_preview.PreviewGenerator.FAIL' - }; - - PreviewGenerator.prototype = { - __proto__: cr.EventTarget.prototype, + constructor( + destinationStore, printTicketStore, nativeLayer, documentInfo, + listenerTracker) { + super(); + + /** + * Used to get the currently selected destination. + * @private {!print_preview.DestinationStore} + */ + this.destinationStore_ = destinationStore; + + /** + * Used to read the state of the ticket and write document information. + * @private {!print_preview.PrintTicketStore} + */ + this.printTicketStore_ = printTicketStore; + + /** + * Interface to the Chromium native layer. + * @private {!print_preview.NativeLayer} + */ + this.nativeLayer_ = nativeLayer; + + /** + * Document data model. + * @private {!print_preview.DocumentInfo} + */ + this.documentInfo_ = documentInfo; + + /** + * ID of current in-flight request. Requests that do not share this ID + * will be ignored. + * @private {number} + */ + this.inFlightRequestId_ = -1; + + /** + * Whether the current in flight request requires generating draft pages + * for print preview. This is true only for modifiable documents when the + * print settings has changed sufficiently to require re-rendering. + * @private {boolean} + */ + this.generateDraft_ = false; + + /** + * Media size to generate preview with. {@code null} indicates default + * size. + * @private {print_preview.ValueType} + */ + this.mediaSize_ = null; + + /** + * Whether the previews are being generated in landscape mode. + * @private {boolean} + */ + this.isLandscapeEnabled_ = false; + + /** + * Whether the previews are being generated with a header and footer. + * @private {boolean} + */ + this.isHeaderFooterEnabled_ = false; + + /** + * Whether the previews are being generated in color. + * @private {boolean} + */ + this.colorValue_ = false; + + /** + * Whether the document should be fitted to the page. + * @private {boolean} + */ + this.isFitToPageEnabled_ = false; + + /** + * The scaling factor (in percent) for the document. Ignored if fit to + * page is true. + * @private {number} + */ + this.scalingValue_ = 100; + + /** + * Page ranges setting used used to generate the last preview. + * @private {Array<{from: number, to: number}>} + */ + this.pageRanges_ = null; + + /** + * Margins type used to generate the last preview. + * @private {!print_preview.ticket_items.MarginsTypeValue} + */ + this.marginsType_ = print_preview.ticket_items.MarginsTypeValue.DEFAULT; + + /** + * Whether the document should have element CSS backgrounds printed. + * @private {boolean} + */ + this.isCssBackgroundEnabled_ = false; + + /** + * Whether the document should have only the selected area printed. + * @private {boolean} + */ + this.isSelectionOnlyEnabled_ = false; + + /** + * Destination that was selected for the last preview. + * @private {print_preview.Destination} + */ + this.selectedDestination_ = null; + + this.addWebUIEventListeners_(listenerTracker); + } /** * Starts listening for relevant WebUI events and adds the listeners to @@ -173,14 +140,14 @@ cr.define('print_preview', function() { * @param {!WebUIListenerTracker} listenerTracker * @private */ - addWebUIEventListeners_: function(listenerTracker) { + addWebUIEventListeners_(listenerTracker) { listenerTracker.add( 'page-count-ready', this.onPageCountReady_.bind(this)); listenerTracker.add( 'page-layout-ready', this.onPageLayoutReady_.bind(this)); listenerTracker.add( 'page-preview-ready', this.onPagePreviewReady_.bind(this)); - }, + } /** * Request that new preview be generated. A preview request will not be @@ -190,13 +157,13 @@ cr.define('print_preview', function() { * was requested, and a promise that will resolve when the preview is * complete (null if no preview was actually requested). */ - requestPreview: function() { + requestPreview() { if (!this.printTicketStore_.isTicketValidForPreview() || !this.printTicketStore_.isInitialized || !this.destinationStore_.selectedDestination) { return {id: -1, request: null}; } - var previewChanged = this.hasPreviewChanged_(); + const previewChanged = this.hasPreviewChanged_(); if (!previewChanged && !this.hasPreviewPageRangeChanged_()) { // Changes to these ticket items might not trigger a new preview, but // they still need to be recorded. @@ -226,7 +193,7 @@ cr.define('print_preview', function() { this.destinationStore_.selectedDestination, this.printTicketStore_, this.documentInfo_, this.generateDraft_, this.inFlightRequestId_), }; - }, + } /** * Dispatches a PAGE_READY event to signal that a page preview is ready. @@ -239,13 +206,13 @@ cr.define('print_preview', function() { * @param {number} previewUid Unique identifier of the preview. * @private */ - dispatchPageReadyEvent_: function(previewIndex, pageNumber, previewUid) { - var pageGenEvent = new Event(PreviewGenerator.EventType.PAGE_READY); + dispatchPageReadyEvent_(previewIndex, pageNumber, previewUid) { + const pageGenEvent = new Event(PreviewGenerator.EventType.PAGE_READY); pageGenEvent.previewIndex = previewIndex; pageGenEvent.previewUrl = 'chrome://print/' + previewUid.toString() + '/' + (pageNumber - 1) + '/print.pdf'; this.dispatchEvent(pageGenEvent); - }, + } /** * Dispatches a PREVIEW_START event. Signals that the preview should be @@ -254,8 +221,8 @@ cr.define('print_preview', function() { * @param {number} index Index of the first page of the preview. * @private */ - dispatchPreviewStartEvent_: function(previewUid, index) { - var previewStartEvent = + dispatchPreviewStartEvent_(previewUid, index) { + const previewStartEvent = new Event(PreviewGenerator.EventType.PREVIEW_START); if (!this.documentInfo_.isModifiable) { index = -1; @@ -263,7 +230,7 @@ cr.define('print_preview', function() { previewStartEvent.previewUrl = 'chrome://print/' + previewUid.toString() + '/' + index + '/print.pdf'; this.dispatchEvent(previewStartEvent); - }, + } /** * @return {boolean} Whether the print ticket, excluding the page range, has @@ -271,8 +238,8 @@ cr.define('print_preview', function() { * should be issued. * @private */ - hasPreviewChanged_: function() { - var ticketStore = this.printTicketStore_; + hasPreviewChanged_() { + const ticketStore = this.printTicketStore_; return this.inFlightRequestId_ == -1 || !ticketStore.mediaSize.isValueEqual(this.mediaSize_) || !ticketStore.landscape.isValueEqual(this.isLandscapeEnabled_) || @@ -293,18 +260,18 @@ cr.define('print_preview', function() { this.isSelectionOnlyEnabled_) || (this.selectedDestination_ != this.destinationStore_.selectedDestination); - }, + } /** * @return {boolean} Whether the page range in the print ticket has changed. * @private */ - hasPreviewPageRangeChanged_: function() { + hasPreviewPageRangeChanged_() { return this.pageRanges_ == null || !areRangesEqual( this.printTicketStore_.pageRange.getPageRanges(), this.pageRanges_); - }, + } /** * Called when the page layout of the document is ready. Always occurs @@ -324,22 +291,22 @@ cr.define('print_preview', function() { * custom page size or style to use. * @private */ - onPageLayoutReady_: function(pageLayout, hasCustomPageSizeStyle) { + onPageLayoutReady_(pageLayout, hasCustomPageSizeStyle) { // NOTE: A request ID is not specified, so assuming its for the current // in-flight request. - var origin = new print_preview.Coordinate2d( + const origin = new print_preview.Coordinate2d( pageLayout.printableAreaX, pageLayout.printableAreaY); - var size = new print_preview.Size( + const size = new print_preview.Size( pageLayout.printableAreaWidth, pageLayout.printableAreaHeight); - var margins = new print_preview.Margins( + const margins = new print_preview.Margins( Math.round(pageLayout.marginTop), Math.round(pageLayout.marginRight), Math.round(pageLayout.marginBottom), Math.round(pageLayout.marginLeft)); - var o = print_preview.ticket_items.CustomMarginsOrientation; - var pageSize = new print_preview.Size( + const o = print_preview.ticket_items.CustomMarginsOrientation; + const pageSize = new print_preview.Size( pageLayout.contentWidth + margins.get(o.LEFT) + margins.get(o.RIGHT), pageLayout.contentHeight + margins.get(o.TOP) + margins.get(o.BOTTOM)); @@ -347,7 +314,7 @@ cr.define('print_preview', function() { this.documentInfo_.updatePageInfo( new print_preview.PrintableArea(origin, size), pageSize, hasCustomPageSizeStyle, margins); - }, + } /** * Called when the document page count is received from the native layer. @@ -359,14 +326,13 @@ cr.define('print_preview', function() { * to page (unused). * @private */ - onPageCountReady_: function( - pageCount, previewResponseId, fitToPageScaling) { + onPageCountReady_(pageCount, previewResponseId, fitToPageScaling) { if (this.inFlightRequestId_ != previewResponseId) { return; // Ignore old response. } this.documentInfo_.updatePageCount(pageCount); this.pageRanges_ = this.printTicketStore_.pageRange.getPageRanges(); - }, + } /** * Called when a page's preview has been generated. Dispatches a @@ -377,20 +343,20 @@ cr.define('print_preview', function() { * preview is a response to. * @private */ - onPagePreviewReady_: function(pageIndex, previewUid, previewResponseId) { + onPagePreviewReady_(pageIndex, previewUid, previewResponseId) { if (this.inFlightRequestId_ != previewResponseId) { return; // Ignore old response. } - var pageNumber = pageIndex + 1; - var pageNumberSet = this.printTicketStore_.pageRange.getPageNumberSet(); + const pageNumber = pageIndex + 1; + const pageNumberSet = this.printTicketStore_.pageRange.getPageNumberSet(); if (pageNumberSet.hasPageNumber(pageNumber)) { - var previewIndex = pageNumberSet.getPageNumberIndex(pageNumber); + const previewIndex = pageNumberSet.getPageNumberIndex(pageNumber); if (previewIndex == 0) { this.dispatchPreviewStartEvent_(previewUid, pageIndex); } this.dispatchPageReadyEvent_(previewIndex, pageNumber, previewUid); } - }, + } /** * Called when the preview generation is complete. Dispatches a @@ -398,7 +364,7 @@ cr.define('print_preview', function() { * @param {number} previewResponseId * @param {number} previewUid */ - onPreviewGenerationDone: function(previewResponseId, previewUid) { + onPreviewGenerationDone(previewResponseId, previewUid) { if (this.inFlightRequestId_ != previewResponseId) { return; // Ignore old response. } @@ -409,17 +375,38 @@ cr.define('print_preview', function() { this.dispatchPreviewStartEvent_(previewUid, 0); } cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY); - }, + } /** * Called when the preview generation fails. * @private */ - onPreviewGenerationFail_: function() { + onPreviewGenerationFail_() { // NOTE: No request ID is returned from Chromium so its assumed its the // current one. cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.FAIL); } + } + + /** + * Event types dispatched by the preview generator. + * @enum {string} + */ + PreviewGenerator.EventType = { + // Dispatched when the document can be printed. + DOCUMENT_READY: 'print_preview.PreviewGenerator.DOCUMENT_READY', + + // Dispatched when a page preview is ready. The previewIndex field of the + // event is the index of the page in the modified document, not the + // original. So page 4 of the original document might be previewIndex = 0 of + // the modified document. + PAGE_READY: 'print_preview.PreviewGenerator.PAGE_READY', + + // Dispatched when the document preview starts to be generated. + PREVIEW_START: 'print_preview.PreviewGenerator.PREVIEW_START', + + // Dispatched when the current print preview request fails. + FAIL: 'print_preview.PreviewGenerator.FAIL' }; // Export diff --git a/chromium/chrome/browser/resources/print_preview/previewarea/margin_control.js b/chromium/chrome/browser/resources/print_preview/previewarea/margin_control.js index 72fe4bca8be..83f9fb86d52 100644 --- a/chromium/chrome/browser/resources/print_preview/previewarea/margin_control.js +++ b/chromium/chrome/browser/resources/print_preview/previewarea/margin_control.js @@ -219,10 +219,11 @@ cr.define('print_preview', function() { */ setPositionInPts: function(posInPts) { this.positionInPts_ = posInPts; - var orientationEnum = print_preview.ticket_items.CustomMarginsOrientation; - var x = this.translateTransform_.x; - var y = this.translateTransform_.y; - var width = null, height = null; + const orientationEnum = + print_preview.ticket_items.CustomMarginsOrientation; + let x = this.translateTransform_.x; + let y = this.translateTransform_.y; + let width = null, height = null; if (this.orientation_ == orientationEnum.TOP) { y = this.scaleTransform_ * posInPts + this.translateTransform_.y - MarginControl.RADIUS_; @@ -268,8 +269,9 @@ cr.define('print_preview', function() { * @return {number} Given value expressed in points. */ convertPixelsToPts: function(pixels) { - var pts; - var orientationEnum = print_preview.ticket_items.CustomMarginsOrientation; + let pts; + const orientationEnum = + print_preview.ticket_items.CustomMarginsOrientation; if (this.orientation_ == orientationEnum.TOP) { pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_; pts /= this.scaleTransform_; @@ -352,7 +354,7 @@ cr.define('print_preview', function() { // and MarginControls are on the single scrollable container. // crbug.com/601341 if (isFocused) { - var previewArea = $('preview-area'); + const previewArea = $('preview-area'); previewArea.scrollTop = 0; previewArea.scrollLeft = 0; } @@ -384,8 +386,8 @@ cr.define('print_preview', function() { onTransitionEnd_: function(event) { if (event.propertyName != 'opacity') return; - var elStyle = window.getComputedStyle(this.getElement()); - var disabled = parseInt(elStyle.getPropertyValue('opacity'), 10) == 0; + const elStyle = window.getComputedStyle(this.getElement()); + const disabled = parseInt(elStyle.getPropertyValue('opacity'), 10) == 0; this.textbox_.setAttribute('aria-hidden', disabled); this.textbox_.disabled = disabled; }, diff --git a/chromium/chrome/browser/resources/print_preview/previewarea/margin_control_container.js b/chromium/chrome/browser/resources/print_preview/previewarea/margin_control_container.js index 9a6f8042c65..248c6b45086 100644 --- a/chromium/chrome/browser/resources/print_preview/previewarea/margin_control_container.js +++ b/chromium/chrome/browser/resources/print_preview/previewarea/margin_control_container.js @@ -68,10 +68,10 @@ cr.define('print_preview', function() { * @private */ this.controls_ = {}; - for (var key in print_preview.ticket_items.CustomMarginsOrientation) { - var orientation = + for (const key in print_preview.ticket_items.CustomMarginsOrientation) { + const orientation = print_preview.ticket_items.CustomMarginsOrientation[key]; - var control = new print_preview.MarginControl(orientation); + const control = new print_preview.MarginControl(orientation); this.controls_[orientation] = control; this.addChild(control); } @@ -145,7 +145,7 @@ cr.define('print_preview', function() { updateTranslationTransform: function(translateTransform) { if (!translateTransform.equals(this.translateTransform_)) { this.translateTransform_ = translateTransform; - for (var orientation in this.controls_) { + for (const orientation in this.controls_) { this.controls_[orientation].setTranslateTransform(translateTransform); } } @@ -158,7 +158,7 @@ cr.define('print_preview', function() { updateScaleTransform: function(scaleTransform) { if (scaleTransform != this.scaleTransform_) { this.scaleTransform_ = scaleTransform; - for (var orientation in this.controls_) { + for (const orientation in this.controls_) { this.controls_[orientation].setScaleTransform(scaleTransform); } } @@ -173,8 +173,8 @@ cr.define('print_preview', function() { return; } this.clippingSize_ = clipSize; - for (var orientation in this.controls_) { - var el = this.controls_[orientation].getElement(); + for (const orientation in this.controls_) { + const el = this.controls_[orientation].getElement(); el.style.clip = 'rect(' + (-el.offsetTop) + 'px, ' + (clipSize.width - el.offsetLeft) + 'px, ' + (clipSize.height - el.offsetTop) + 'px, ' + (-el.offsetLeft) + @@ -215,7 +215,7 @@ cr.define('print_preview', function() { print_preview.ticket_items.TicketItem.EventType.CHANGE, this.onTicketChange_.bind(this)); - for (var orientation in this.controls_) { + for (const orientation in this.controls_) { this.tracker.add( this.controls_[orientation], print_preview.MarginControl.EventType.DRAG_START, @@ -229,7 +229,7 @@ cr.define('print_preview', function() { /** @override */ decorateInternal: function() { - for (var orientation in this.controls_) { + for (const orientation in this.controls_) { this.controls_[orientation].render(this.getElement()); } }, @@ -239,7 +239,7 @@ cr.define('print_preview', function() { * @private */ setIsMarginControlsVisible_: function(isVisible) { - for (var orientation in this.controls_) { + for (const orientation in this.controls_) { this.controls_[orientation].setIsVisible(isVisible); } }, @@ -253,7 +253,7 @@ cr.define('print_preview', function() { * @private */ moveControlWithConstraints_: function(control, posInPixels) { - var newPosInPts; + let newPosInPts; if (MarginControlContainer.isTopOrBottom_(control.getOrientation())) { newPosInPts = control.convertPixelsToPts(posInPixels.y); } else { @@ -279,14 +279,14 @@ cr.define('print_preview', function() { if (value.length == 0) { return null; } - var validationRegex = new RegExp( + const validationRegex = new RegExp( '^(^-?)(\\d)+(\\' + this.measurementSystem_.thousandsDelimeter + '\\d{3})*(\\' + this.measurementSystem_.decimalDelimeter + '\\d*)?' + '(' + this.measurementSystem_.unitSymbol + ')?$'); if (validationRegex.test(value)) { // Replacing decimal point with the dot symbol in order to use // parseFloat() properly. - var replacementRegex = + const replacementRegex = new RegExp('\\' + this.measurementSystem_.decimalDelimeter + '{1}'); value = value.replace(replacementRegex, '.'); return this.measurementSystem_.convertToPoints(parseFloat(value)); @@ -350,7 +350,7 @@ cr.define('print_preview', function() { this.getElement().classList.remove( MarginControlContainer.Classes_.DRAGGING_HORIZONTAL); if (event) { - var posInPixels = + const posInPixels = this.draggedControl_.translateMouseToPositionInPixels( new print_preview.Coordinate2d(event.x, event.y)); this.moveControlWithConstraints_(this.draggedControl_, posInPixels); @@ -371,7 +371,7 @@ cr.define('print_preview', function() { * @private */ onMouseOver_: function(event) { - var fromElement = event.fromElement; + let fromElement = event.fromElement; while (fromElement != null) { if (fromElement == this.getElement()) { return; @@ -392,7 +392,7 @@ cr.define('print_preview', function() { * @private */ onMouseOut_: function(event) { - var toElement = event.toElement; + let toElement = event.toElement; while (toElement != null) { if (toElement == this.getElement()) { return; @@ -402,7 +402,7 @@ cr.define('print_preview', function() { if (this.draggedControl_ != null) { return; } - for (var orientation in this.controls_) { + for (const orientation in this.controls_) { if (this.controls_[orientation].getIsFocused() || this.controls_[orientation].getIsInError()) { return; @@ -417,9 +417,9 @@ cr.define('print_preview', function() { * @private */ onTicketChange_: function() { - var margins = this.customMarginsTicketItem_.getValue(); - for (var orientation in this.controls_) { - var control = this.controls_[orientation]; + const margins = this.customMarginsTicketItem_.getValue(); + for (const orientation in this.controls_) { + const control = this.controls_[orientation]; control.setPageSize(this.documentInfo_.pageSize); control.setTextboxValue( this.serializeValueFromPts_(margins.get(orientation))); @@ -442,17 +442,17 @@ cr.define('print_preview', function() { * @private */ onControlTextChange_: function(control) { - var marginValue = this.parseValueToPts_(control.getTextboxValue()); + const marginValue = this.parseValueToPts_(control.getTextboxValue()); if (marginValue != null) { this.customMarginsTicketItem_.updateMargin( control.getOrientation(), marginValue); // Enable all controls. - for (var o in this.controls_) { + for (const o in this.controls_) { this.controls_[o].setIsEnabled(true); } control.setIsInError(false); } else { - var enableOtherControls; + let enableOtherControls; if (!control.getIsFocused()) { // If control no longer in focus, revert to previous valid value. control.setTextboxValue( @@ -464,7 +464,7 @@ cr.define('print_preview', function() { enableOtherControls = false; } // Enable other controls. - for (var o in this.controls_) { + for (const o in this.controls_) { if (control.getOrientation() != o) { this.controls_[o].setIsEnabled(enableOtherControls); } 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 98e8f1c9add..cde64b46ac0 100644 --- a/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js +++ b/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js @@ -266,7 +266,7 @@ cr.define('print_preview', function() { } // Don't handle the key event for these elements. - var tagName = document.activeElement.tagName; + const tagName = document.activeElement.tagName; if (arrayContains(['INPUT', 'SELECT', 'EMBED'], tagName)) { return; } @@ -276,7 +276,7 @@ cr.define('print_preview', function() { // element, and work up the DOM tree to see if any element has a // scrollbar. If there exists a scrollbar, do not handle the key event // here. - var element = e.target; + let element = e.target; while (element) { if (element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth) { @@ -317,7 +317,7 @@ cr.define('print_preview', function() { assert(this.openSystemDialogButton_), 'click', this.onOpenSystemDialogButtonClick_.bind(this)); - var TicketStoreEvent = print_preview.PrintTicketStore.EventType; + const TicketStoreEvent = print_preview.PrintTicketStore.EventType; [TicketStoreEvent.INITIALIZE, TicketStoreEvent.CAPABILITIES_CHANGE, TicketStoreEvent.DOCUMENT_CHANGE] .forEach(eventType => { @@ -386,9 +386,9 @@ cr.define('print_preview', function() { // TODO(raymes): It's harder to test compatibility of the out of process // plugin because it's asynchronous. We could do a better job at some // point. - var oopCompatObj = this.getElement().getElementsByClassName( + const oopCompatObj = this.getElement().getElementsByClassName( PreviewArea.Classes_.OUT_OF_PROCESS_COMPATIBILITY_OBJECT)[0]; - var isOOPCompatible = oopCompatObj.postMessage; + const isOOPCompatible = oopCompatObj.postMessage; oopCompatObj.parentElement.removeChild(oopCompatObj); return isOOPCompatible; @@ -404,25 +404,25 @@ cr.define('print_preview', function() { */ showMessage_: function(messageId, opt_message) { // Hide all messages. - var messageEls = this.getElement().getElementsByClassName( + const messageEls = this.getElement().getElementsByClassName( PreviewArea.Classes_.MESSAGE); - for (var i = 0, messageEl; (messageEl = messageEls[i]); i++) { + for (let i = 0, messageEl; (messageEl = messageEls[i]); i++) { setIsVisible(messageEl, false); } // Disable jumping animation to conserve cycles. - var jumpingDotsEl = this.getElement().querySelector( + const jumpingDotsEl = this.getElement().querySelector( '.preview-area-loading-message-jumping-dots'); jumpingDotsEl.classList.remove('jumping-dots'); // Show specific message. if (messageId == print_preview.PreviewAreaMessageId_.CUSTOM) { - var customMessageTextEl = this.getElement().getElementsByClassName( + const customMessageTextEl = this.getElement().getElementsByClassName( PreviewArea.Classes_.CUSTOM_MESSAGE_TEXT)[0]; customMessageTextEl.textContent = opt_message; } else if (messageId == print_preview.PreviewAreaMessageId_.LOADING) { jumpingDotsEl.classList.add('jumping-dots'); } - var messageEl = this.getElement().getElementsByClassName( + const messageEl = this.getElement().getElementsByClassName( PreviewArea.MessageIdClassMap_[messageId])[0]; setIsVisible(messageEl, true); @@ -440,20 +440,20 @@ cr.define('print_preview', function() { this.overlayEl_.setAttribute('aria-hidden', !visible); // Hide/show all controls that will overlap when the overlay is visible. - var marginControls = this.getElement().getElementsByClassName( + const marginControls = this.getElement().getElementsByClassName( PreviewArea.Classes_.MARGIN_CONTROL); - for (var i = 0; i < marginControls.length; ++i) { + for (let i = 0; i < marginControls.length; ++i) { marginControls[i].setAttribute('aria-hidden', visible); } - var previewAreaControls = this.getElement().getElementsByClassName( + const previewAreaControls = this.getElement().getElementsByClassName( PreviewArea.Classes_.PREVIEW_AREA); - for (var i = 0; i < previewAreaControls.length; ++i) { + for (let i = 0; i < previewAreaControls.length; ++i) { previewAreaControls[i].setAttribute('aria-hidden', visible); } if (!visible) { // Disable jumping animation to conserve cycles. - var jumpingDotsEl = this.getElement().querySelector( + const jumpingDotsEl = this.getElement().querySelector( '.preview-area-loading-message-jumping-dots'); jumpingDotsEl.classList.remove('jumping-dots'); } @@ -505,7 +505,7 @@ cr.define('print_preview', function() { */ onOpenSystemDialogButtonClick_: function() { this.openSystemDialogButton_.disabled = true; - var openSystemDialogThrobber = this.getElement().getElementsByClassName( + const openSystemDialogThrobber = this.getElement().getElementsByClassName( PreviewArea.Classes_.OPEN_SYSTEM_DIALOG_BUTTON_THROBBER)[0]; setIsVisible(openSystemDialogThrobber, true); cr.dispatchSimpleEvent( @@ -519,7 +519,7 @@ cr.define('print_preview', function() { onTicketChange_: function() { if (!this.previewGenerator_) return; - var previewRequest = this.previewGenerator_.requestPreview(); + const previewRequest = this.previewGenerator_.requestPreview(); if (previewRequest.id <= -1) { this.marginControlContainer_.showMarginControlsIfNeeded(); return; diff --git a/chromium/chrome/browser/resources/print_preview/print_header.js b/chromium/chrome/browser/resources/print_preview/print_header.js index b8f12871fe8..67e445bd70f 100644 --- a/chromium/chrome/browser/resources/print_preview/print_header.js +++ b/chromium/chrome/browser/resources/print_preview/print_header.js @@ -80,7 +80,7 @@ cr.define('print_preview', function() { /** @param {string} message Error message to display in the print header. */ setErrorMessage: function(message) { - var summaryEl = this.getChildElement('.summary'); + const summaryEl = this.getChildElement('.summary'); summaryEl.innerHTML = ''; summaryEl.textContent = message; this.getChildElement('button.print').classList.toggle('loading', false); @@ -144,24 +144,24 @@ cr.define('print_preview', function() { return; } - var saveToPdfOrDrive = this.destinationStore_.selectedDestination && + const saveToPdfOrDrive = this.destinationStore_.selectedDestination && (this.destinationStore_.selectedDestination.id == print_preview.Destination.GooglePromotedId.SAVE_AS_PDF || this.destinationStore_.selectedDestination.id == print_preview.Destination.GooglePromotedId.DOCS); - var numPages = this.printTicketStore_.pageRange.getPageNumberSet().size; - var numSheets = numPages; + let numPages = this.printTicketStore_.pageRange.getPageNumberSet().size; + let numSheets = numPages; if (!saveToPdfOrDrive && this.printTicketStore_.duplex.getValue()) { numSheets = Math.ceil(numPages / 2); } - var copies = this.printTicketStore_.copies.getValueAsNumber(); + const copies = this.printTicketStore_.copies.getValueAsNumber(); numSheets *= copies; numPages *= copies; - var pagesLabel = loadTimeData.getString('printPreviewPageLabelPlural'); - var summaryLabel; + const pagesLabel = loadTimeData.getString('printPreviewPageLabelPlural'); + let summaryLabel; if (numSheets > 1) { summaryLabel = saveToPdfOrDrive ? pagesLabel : @@ -172,8 +172,8 @@ cr.define('print_preview', function() { 'printPreviewSheetsLabelSingular'); } - var html; - var label; + let html; + let label; if (numPages != numSheets) { html = loadTimeData.getStringF( 'printPreviewSummaryFormatLong', @@ -196,7 +196,7 @@ cr.define('print_preview', function() { // Removing extra spaces from within the string. html = html.replace(/\s{2,}/g, ' '); - var summary = this.getChildElement('.summary'); + const summary = this.getChildElement('.summary'); summary.innerHTML = html; summary.setAttribute('aria-label', label); }, @@ -211,7 +211,7 @@ cr.define('print_preview', function() { print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) { this.getChildElement('button.print').classList.add('loading'); this.getChildElement('button.cancel').classList.add('loading'); - var isSaveLabel = + const isSaveLabel = (this.destinationStore_.selectedDestination.id == print_preview.Destination.GooglePromotedId.DOCS); this.getChildElement('.summary').innerHTML = @@ -235,7 +235,7 @@ cr.define('print_preview', function() { * @private */ onDestinationSelect_: function() { - var isSaveLabel = this.destinationStore_.selectedDestination && + const isSaveLabel = this.destinationStore_.selectedDestination && (this.destinationStore_.selectedDestination.id == print_preview.Destination.GooglePromotedId.SAVE_AS_PDF || this.destinationStore_.selectedDestination.id == diff --git a/chromium/chrome/browser/resources/print_preview/print_preview.js b/chromium/chrome/browser/resources/print_preview/print_preview.js index bee9f809d16..2aa5685083f 100644 --- a/chromium/chrome/browser/resources/print_preview/print_preview.js +++ b/chromium/chrome/browser/resources/print_preview/print_preview.js @@ -40,7 +40,7 @@ print_preview.PrintAttemptResult_ = { cr.define('print_preview', function() { 'use strict'; - var PrintPreviewUiState_ = print_preview.PrintPreviewUiState_; + const PrintPreviewUiState_ = print_preview.PrintPreviewUiState_; /** * Container class for Chromium's print preview. @@ -229,7 +229,7 @@ cr.define('print_preview', function() { new print_preview.AdvancedSettings(this.printTicketStore_); this.addChild(this.advancedSettings_); - var settingsSections = [ + const settingsSections = [ this.destinationSettings_, this.pageSettings_, this.copiesSettings_, this.mediaSizeSettings_, this.layoutSettings_, this.marginSettings_, this.colorSettings_, this.dpiSettings_, this.scalingSettings_, @@ -416,12 +416,8 @@ cr.define('print_preview', function() { this.tracker.add( this.destinationSearch_, - print_preview.DestinationSearch.EventType.MANAGE_CLOUD_DESTINATIONS, - this.onManageCloudDestinationsActivated_.bind(this)); - this.tracker.add( - this.destinationSearch_, - print_preview.DestinationSearch.EventType.MANAGE_LOCAL_DESTINATIONS, - this.onManageLocalDestinationsActivated_.bind(this)); + print_preview.DestinationSearch.EventType.MANAGE_PRINT_DESTINATIONS, + this.onManagePrintDestinationsActivated_.bind(this)); this.tracker.add( this.destinationSearch_, print_preview.DestinationSearch.EventType.ADD_ACCOUNT, @@ -516,7 +512,7 @@ cr.define('print_preview', function() { } this.setIsEnabled_(false); this.printHeader_.isCancelButtonEnabled = true; - var printAttemptResult = this.printIfReady_(); + const printAttemptResult = this.printIfReady_(); if (printAttemptResult == print_preview.PrintAttemptResult_.READY_WAITING_FOR_PREVIEW) { if ((this.destinationStore_.selectedDestination.isLocal && @@ -538,7 +534,7 @@ cr.define('print_preview', function() { * @private */ printIfReady_: function() { - var okToPrint = + const okToPrint = (this.uiState_ == PrintPreviewUiState_.PRINTING || this.uiState_ == PrintPreviewUiState_.OPENING_PDF_PREVIEW || this.uiState_ == PrintPreviewUiState_.FILE_SELECTION || @@ -562,10 +558,9 @@ cr.define('print_preview', function() { print_preview.Metrics.PrintSettingsUiBucket .PRINT_WITH_SETTINGS_COLLAPSED); } - var destination = assert(this.destinationStore_.selectedDestination); - var whenPrintDone = this.nativeLayer_.print( - destination, this.printTicketStore_, this.cloudPrintInterface_, - this.documentInfo_, + const destination = assert(this.destinationStore_.selectedDestination); + const whenPrintDone = this.nativeLayer_.print( + destination, this.printTicketStore_, this.documentInfo_, this.uiState_ == PrintPreviewUiState_.OPENING_PDF_PREVIEW, this.showSystemDialogBeforeNextPrint_); if (this.uiState_ == PrintPreviewUiState_.OPENING_PDF_PREVIEW || @@ -575,16 +570,16 @@ cr.define('print_preview', function() { print_preview.Destination.GooglePromotedId.SAVE_AS_PDF)) { // Local printers resolve when print is ready to start. Hide the // dialog. Mac "Open in Preview" is treated as a local printer. - var boundHideDialog = () => { + const boundHideDialog = () => { this.nativeLayer_.hidePreview(); }; whenPrintDone.then(boundHideDialog, boundHideDialog); } else if (!destination.isLocal) { // Cloud print resolves when print data is returned to submit to cloud - // print, or if setings are invalid. + // print, or if print ticket cannot be read, no PDF data is found, or + // PDF is oversized. whenPrintDone.then( - this.onPrintToCloud_.bind(this), - this.onSettingsInvalid_.bind(this)); + this.onPrintToCloud_.bind(this), this.onPrintFailed_.bind(this)); } else if (destination.isPrivet || destination.isExtension) { // Privet and extension resolve when printing is complete or if there // is an error printing. @@ -668,6 +663,7 @@ cr.define('print_preview', function() { settings.serializedDefaultDestinationSelectionRulesStr); this.appState_.setInitialized(); + // This is only visible in the task manager. $('document-title').innerText = settings.documentTitle; this.hideSystemDialogLink_ = settings.isInAppKioskMode; if ($('system-dialog-link')) { @@ -861,7 +857,7 @@ cr.define('print_preview', function() { * @private */ onCloudPrintRegisterPromoClick_: function(e) { - var devicesUrl = 'chrome://devices/register?id=' + e.destination.id; + const devicesUrl = 'chrome://devices/register?id=' + e.destination.id; this.nativeLayer_.forceOpenNewTab(devicesUrl); this.destinationStore_.waitForRegister(e.destination.id); }, @@ -912,7 +908,7 @@ cr.define('print_preview', function() { assert( this.uiState_ == PrintPreviewUiState_.READY, 'Trying to print when not in ready state: ' + this.uiState_); - var activeElementTag = document.activeElement.tagName.toUpperCase(); + const activeElementTag = document.activeElement.tagName.toUpperCase(); if (activeElementTag != 'BUTTON' && activeElementTag != 'SELECT' && activeElementTag != 'A') { this.printDocumentOrOpenPdfPreview_(false /*isPdfPreview*/); @@ -954,7 +950,7 @@ cr.define('print_preview', function() { */ onTicketChange_: function() { this.printHeader_.onTicketChange(); - var disable = !this.printHeader_.isPrintButtonEnabled; + const disable = !this.printHeader_.isPrintButtonEnabled; if (cr.isWindows && $('system-dialog-link')) $('system-dialog-link').disabled = disable; if ($('open-pdf-in-preview-link')) @@ -981,21 +977,12 @@ cr.define('print_preview', function() { }, /** - * Called when the destination search dispatches manage cloud destinations - * event. Calls corresponding native layer method. - * @private - */ - onManageCloudDestinationsActivated_: function() { - this.nativeLayer_.manageCloudPrinters(this.userInfo_.activeUser); - }, - - /** - * Called when the destination search dispatches manage local destinations - * event. Calls corresponding native layer method. + * Called when the destination search dispatches manage all print + * destinations event. Calls corresponding native layer method. * @private */ - onManageLocalDestinationsActivated_: function() { - this.nativeLayer_.manageLocalPrinters(); + onManagePrintDestinationsActivated_: function() { + this.nativeLayer_.managePrinters(); }, /** @@ -1050,13 +1037,13 @@ cr.define('print_preview', function() { }, /** - * Called when printing to a privet or extension printer fails. + * Called when printing to a privet, cloud, or extension printer fails. * @param {*} httpError The HTTP error code, or -1 or a string describing * the error, if not an HTTP error. * @private */ onPrintFailed_: function(httpError) { - console.error('Privet printing failed with error code ' + httpError); + console.error('Printing failed with error code ' + httpError); this.printHeader_.setErrorMessage( loadTimeData.getString('couldNotPrint')); }, @@ -1109,9 +1096,9 @@ cr.define('print_preview', function() { return; } - var destinations = this.destinationStore_.destinations(); - var pdfDestination = null; - for (var i = 0; i < destinations.length; i++) { + const destinations = this.destinationStore_.destinations(); + let pdfDestination = null; + for (let i = 0; i < destinations.length; i++) { if (destinations[i].id == print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) { pdfDestination = destinations[i]; @@ -1133,7 +1120,7 @@ cr.define('print_preview', function() { * @private */ setLayoutSettingsForTest_: function(portrait) { - var combobox = document.querySelector('.layout-settings-select'); + const combobox = document.querySelector('.layout-settings-select'); if (combobox.value == 'portrait') { this.nativeLayer_.uiLoadedForTest(); } else { @@ -1150,7 +1137,7 @@ cr.define('print_preview', function() { * @private */ setPageRangeForTest_: function(pageRange) { - var textbox = document.querySelector('.page-settings-custom-input'); + const textbox = document.querySelector('.page-settings-custom-input'); if (textbox.value == pageRange) { this.nativeLayer_.uiLoadedForTest(); } else { @@ -1167,7 +1154,7 @@ cr.define('print_preview', function() { * @private */ setHeadersAndFootersForTest_: function(headersAndFooters) { - var checkbox = document.querySelector('.header-footer-checkbox'); + const checkbox = document.querySelector('.header-footer-checkbox'); if (headersAndFooters == checkbox.checked) this.nativeLayer_.uiLoadedForTest(); else @@ -1182,7 +1169,7 @@ cr.define('print_preview', function() { * @private */ setBackgroundColorsAndImagesForTest_: function(backgroundColorsAndImages) { - var checkbox = document.querySelector('.css-background-checkbox'); + const checkbox = document.querySelector('.css-background-checkbox'); if (backgroundColorsAndImages == checkbox.checked) this.nativeLayer_.uiLoadedForTest(); else @@ -1197,7 +1184,7 @@ cr.define('print_preview', function() { * @private */ setMarginsForTest_: function(margins) { - var combobox = document.querySelector('.margin-settings-select'); + const combobox = document.querySelector('.margin-settings-select'); if (margins == combobox.selectedIndex) { this.nativeLayer_.uiLoadedForTest(); } else if (margins >= 0 && margins < combobox.length) { @@ -1218,7 +1205,7 @@ cr.define('print_preview', function() { return false; if (!cr.isWindows) return true; - var selectedDest = this.destinationStore_.selectedDestination; + const selectedDest = this.destinationStore_.selectedDestination; return !!selectedDest && selectedDest.origin == print_preview.DestinationOrigin.LOCAL && selectedDest.id != @@ -1326,16 +1313,15 @@ cr.define('print_preview', function() { // <include src="preview_generator.js"> // <include src="search/destination_list.js"> -// <include src="search/cloud_destination_list.js"> // <include src="search/recent_destination_list.js"> // <include src="search/destination_list_item.js"> // <include src="search/destination_search.js"> // <include src="search/provisional_destination_resolver.js"> window.addEventListener('DOMContentLoaded', function() { - var previewWindow = /** @type {{isTest: boolean}} */ (window); + const previewWindow = /** @type {{isTest: boolean}} */ (window); if (!previewWindow.isTest) { - var printPreview = new print_preview.PrintPreview(); + const printPreview = new print_preview.PrintPreview(); printPreview.initialize(); } }); diff --git a/chromium/chrome/browser/resources/print_preview/print_preview_animations.js b/chromium/chrome/browser/resources/print_preview/print_preview_animations.js index 86ead3a16b1..f2d9f0f5c5b 100644 --- a/chromium/chrome/browser/resources/print_preview/print_preview_animations.js +++ b/chromium/chrome/browser/resources/print_preview/print_preview_animations.js @@ -3,15 +3,16 @@ // found in the LICENSE file. // Counter used to give animations unique names. -var animationCounter = 0; +let animationCounter = 0; -var animationEventTracker = new EventTracker(); +const animationEventTracker = new EventTracker(); function addAnimation(code) { - var name = 'anim' + animationCounter; + const name = 'anim' + animationCounter; animationCounter++; - var rules = document.createTextNode('@keyframes ' + name + ' {' + code + '}'); - var el = document.createElement('style'); + const rules = + document.createTextNode('@keyframes ' + name + ' {' + code + '}'); + const el = document.createElement('style'); el.type = 'text/css'; el.appendChild(rules); el.setAttribute('id', name); @@ -46,13 +47,13 @@ function fadeInElement(el, opt_justShow) { el.hidden = false; el.setAttribute('aria-hidden', 'false'); el.style.height = 'auto'; - var height = el.offsetHeight; + const height = el.offsetHeight; if (opt_justShow) { el.style.height = ''; el.style.opacity = ''; } else { el.style.height = height + 'px'; - var animName = addAnimation(getFadeInAnimationCode(height)); + const animName = addAnimation(getFadeInAnimationCode(height)); animationEventTracker.add( el, 'animationend', onFadeInAnimationEnd.bind(el), false); el.style.animationName = animName; @@ -70,7 +71,7 @@ function fadeOutElement(el) { return; fadeInAnimationCleanup(el); el.style.height = 'auto'; - var height = el.offsetHeight; + const height = el.offsetHeight; el.style.height = height + 'px'; /** @suppress {suspiciousCode} */ el.offsetHeight; // Should force an update of the computed style. @@ -109,7 +110,7 @@ function onFadeInAnimationEnd(event) { */ function fadeInAnimationCleanup(element) { if (element.style.animationName) { - var animEl = $(element.style.animationName); + const animEl = $(element.style.animationName); if (animEl) animEl.parentNode.removeChild(animEl); element.style.animationName = ''; @@ -129,15 +130,15 @@ function fadeInOption(el, opt_justShow) { // To make the option visible during the first fade in. el.hidden = false; - var leftColumn = + const leftColumn = assertInstanceof(el.querySelector('.left-column'), HTMLElement); wrapContentsInDiv(leftColumn, ['invisible']); - var rightColumn = + const rightColumn = assertInstanceof(el.querySelector('.right-column'), HTMLElement); wrapContentsInDiv(rightColumn, ['invisible']); - var toAnimate = el.querySelectorAll('.collapsible'); - for (var i = 0; i < toAnimate.length; i++) + const toAnimate = el.querySelectorAll('.collapsible'); + for (let i = 0; i < toAnimate.length; i++) fadeInElement(assertInstanceof(toAnimate[i], HTMLElement), opt_justShow); el.classList.add('visible'); } @@ -152,16 +153,16 @@ function fadeOutOption(el, opt_justHide) { if (!el.classList.contains('visible')) return; - var leftColumn = + const leftColumn = assertInstanceof(el.querySelector('.left-column'), HTMLElement); wrapContentsInDiv(leftColumn, ['visible']); - var rightColumn = + const rightColumn = assertInstanceof(el.querySelector('.right-column'), HTMLElement); if (rightColumn) wrapContentsInDiv(rightColumn, ['visible']); - var toAnimate = el.querySelectorAll('.collapsible'); - for (var i = 0; i < toAnimate.length; i++) { + const toAnimate = el.querySelectorAll('.collapsible'); + for (let i = 0; i < toAnimate.length; i++) { if (opt_justHide) { toAnimate[i].hidden = true; toAnimate[i].classList.add('closing'); @@ -181,7 +182,7 @@ function fadeOutOption(el, opt_justHide) { * @param {!Array} classes The css classes to add. */ function wrapContentsInDiv(el, classes) { - var div = el.querySelector('div'); + let div = el.querySelector('div'); if (!div || !div.classList.contains('collapsible')) { div = document.createElement('div'); while (el.childNodes.length > 0) @@ -191,6 +192,6 @@ function wrapContentsInDiv(el, classes) { div.className = ''; div.classList.add('collapsible'); - for (var i = 0; i < classes.length; i++) + for (let i = 0; i < classes.length; i++) div.classList.add(classes[i]); } diff --git a/chromium/chrome/browser/resources/print_preview/print_preview_focus_manager.js b/chromium/chrome/browser/resources/print_preview/print_preview_focus_manager.js index 47bb04b81d1..e8ee6ce4366 100644 --- a/chromium/chrome/browser/resources/print_preview/print_preview_focus_manager.js +++ b/chromium/chrome/browser/resources/print_preview/print_preview_focus_manager.js @@ -20,8 +20,8 @@ cr.define('print_preview', function() { /** @override */ getFocusParent: function() { - var el = document.body; - var newEl = null; + let el = document.body; + let newEl = null; while (newEl = el.querySelector('.overlay:not([hidden])')) el = newEl; return el; diff --git a/chromium/chrome/browser/resources/print_preview/print_preview_new.html b/chromium/chrome/browser/resources/print_preview/print_preview_new.html new file mode 100644 index 00000000000..303438ada23 --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/print_preview_new.html @@ -0,0 +1,21 @@ +<!doctype html> +<html dir="$i18n{textdirection}" lang="$i18n{language}"> +<head> +<meta charset="utf-8"> + <title></title> +</head> +<body> + <style> + html, + body { + height: 100%; + margin: 0; + overflow: hidden; + width: 100%; + } + </style> + <print-preview-app></print-preview-app> + <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> + <link rel="import" href="new/app.html"> +</body> +</html> diff --git a/chromium/chrome/browser/resources/print_preview/print_preview_resources.grd b/chromium/chrome/browser/resources/print_preview/print_preview_resources.grd new file mode 100644 index 00000000000..9066a7ea12c --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/print_preview_resources.grd @@ -0,0 +1,185 @@ +<?xml version="1.0" encoding="UTF-8"?> +<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> + <outputs> + <output filename="grit/print_preview_resources.h" type="rc_header"> + <emit emit_type='prepend'></emit> + </output> + <output filename="grit/print_preview_resources_map.cc" + type="resource_file_map_source" /> + <output filename="grit/print_preview_resources_map.h" + type="resource_map_header" /> + <output filename="print_preview_resources.pak" type="data_package" /> + </outputs> + <release seq="1"> + <structures> + <structure name="IDR_PRINT_PREVIEW_NEW_HTML" + file="print_preview_new.html" + flattenhtml="true" + allowexternalscript="true" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_APP_HTML" + file="new/app.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_APP_JS" + file="new/app.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_MODEL_HTML" + file="new/model.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_MODEL_JS" + file="new/model.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_DATA_DESTINATION_HTML" + file="data/destination.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_DATA_DESTINATION_JS" + file="data/destination.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_PRINT_PREVIEW_UTILS_HTML" + file="print_preview_utils.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_PRINT_PREVIEW_UTILS_JS" + file="print_preview_utils.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_DATA_DOCUMENT_INFO_HTML" + file="data/document_info.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_DATA_DOCUMENT_INFO_JS" + file="data/document_info.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_DATA_SIZE_HTML" + file="data/size.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_DATA_SIZE_JS" + file="data/size.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_DATA_COORDINATE2D_HTML" + file="data/coordinate2d.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_DATA_COORDINATE2D_JS" + file="data/coordinate2d.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_DATA_PRINTABLE_AREA_HTML" + file="data/printable_area.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_DATA_PRINTABLE_AREA_JS" + file="data/printable_area.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_HEADER_HTML" + file="new/header.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_HEADER_JS" + file="new/header.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_SETTINGS_BEHAVIOR_HTML" + file="new/settings_behavior.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_SETTINGS_BEHAVIOR_JS" + file="new/settings_behavior.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_SETTINGS_SECTION_HTML" + file="new/settings_section.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_SETTINGS_SECTION_JS" + file="new/settings_section.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_SETTINGS_HTML" + file="new/destination_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_SETTINGS_JS" + file="new/destination_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_PAGES_SETTINGS_HTML" + file="new/pages_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_PAGES_SETTINGS_JS" + file="new/pages_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_COPIES_SETTINGS_HTML" + file="new/copies_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_COPIES_SETTINGS_JS" + file="new/copies_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_LAYOUT_SETTINGS_HTML" + file="new/layout_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_LAYOUT_SETTINGS_JS" + file="new/layout_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_COLOR_SETTINGS_HTML" + file="new/color_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_COLOR_SETTINGS_JS" + file="new/color_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_MEDIA_SIZE_SETTINGS_HTML" + file="new/media_size_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_MEDIA_SIZE_SETTINGS_JS" + file="new/media_size_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_MARGINS_SETTINGS_HTML" + file="new/margins_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_MARGINS_SETTINGS_JS" + file="new/margins_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_DPI_SETTINGS_HTML" + file="new/dpi_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_DPI_SETTINGS_JS" + file="new/dpi_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_SCALING_SETTINGS_HTML" + file="new/scaling_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_SCALING_SETTINGS_JS" + file="new/scaling_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_OTHER_OPTIONS_SETTINGS_HTML" + file="new/other_options_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_OTHER_OPTIONS_SETTINGS_JS" + file="new/other_options_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_ADVANCED_OPTIONS_SETTINGS_HTML" + file="new/advanced_options_settings.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_ADVANCED_OPTIONS_SETTINGS_JS" + file="new/advanced_options_settings.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_NUMBER_SETTINGS_SECTION_HTML" + file="new/number_settings_section.html" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_NUMBER_SETTINGS_SECTION_JS" + file="new/number_settings_section.js" + type="chrome_html" /> + <structure name="IDR_PRINT_PREVIEW_NEW_PRINT_PREVIEW_SHARED_CSS_HTML" + file="new/print_preview_shared_css.html" + type="chrome_html" + preprocess="true" /> + <structure name="IDR_PRINT_PREVIEW_NEW_BUTTON_CSS_HTML" + file="new/button_css.html" + type="chrome_html" + preprocess="true"/> + <structure name="IDR_PRINT_PREVIEW_NEW_SELECT_CSS_HTML" + file="new/select_css.html" + type="chrome_html" + preprocess="true"/> + <structure name="IDR_PRINT_PREVIEW_NEW_CHECKBOX_RADIO_CSS_HTML" + file="new/checkbox_radio_css.html" + type="chrome_html" + preprocess="true"/> + <structure name="IDR_PRINT_PREVIEW_NEW_INPUT_CSS_HTML" + file="new/input_css.html" + type="chrome_html"/> + <structure name="IDR_PRINT_PREVIEW_NEW_THROBBER_CSS_HTML" + file="new/throbber_css.html" + type="chrome_html"/> + <structure name="IDR_PRINT_PREVIEW_NEW_STRINGS_HTML" + file="new/strings.html" + type="chrome_html" /> + </structures> + </release> +</grit> diff --git a/chromium/chrome/browser/resources/print_preview/print_preview_utils.html b/chromium/chrome/browser/resources/print_preview/print_preview_utils.html new file mode 100644 index 00000000000..6c28ddc323d --- /dev/null +++ b/chromium/chrome/browser/resources/print_preview/print_preview_utils.html @@ -0,0 +1,3 @@ +<link rel="import" href="chrome://resources/html/cr.html"> + +<script src="print_preview_utils.js"></script> 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 bfd199c3801..cd931980ba4 100644 --- a/chromium/chrome/browser/resources/print_preview/print_preview_utils.js +++ b/chromium/chrome/browser/resources/print_preview/print_preview_utils.js @@ -8,7 +8,7 @@ * whitespace is allowed. */ function isInteger(toTest) { - var numericExp = /^\s*[0-9]+\s*$/; + const numericExp = /^\s*[0-9]+\s*$/; return numericExp.test(toTest); } @@ -30,7 +30,7 @@ function isPositiveInteger(value) { function areArraysEqual(array1, array2) { if (array1.length != array2.length) return false; - for (var i = 0; i < array1.length; i++) + for (let i = 0; i < array1.length; i++) if (array1[i] !== array2[i]) return false; return true; @@ -45,7 +45,7 @@ function areArraysEqual(array1, array2) { function areRangesEqual(array1, array2) { if (array1.length != array2.length) return false; - for (var i = 0; i < array1.length; i++) + for (let i = 0; i < array1.length; i++) if (array1[i].from != array2[i].from || array1[i].to != array2[i].to) { return false; } @@ -59,20 +59,24 @@ function areRangesEqual(array1, array2) { * @return {!Array<number>} The array after processing. */ function removeDuplicates(inArray) { - var out = []; + const out = []; if (inArray.length == 0) return out; out.push(inArray[0]); - for (var i = 1; i < inArray.length; ++i) + for (let i = 1; i < inArray.length; ++i) if (inArray[i] != inArray[i - 1]) out.push(inArray[i]); return out; } /** @enum {number} */ -var PageRangeStatus = {NO_ERROR: 0, SYNTAX_ERROR: -1, LIMIT_ERROR: -2}; +const PageRangeStatus = { + NO_ERROR: 0, + SYNTAX_ERROR: -1, + LIMIT_ERROR: -2 +}; /** * Returns a list of ranges in |pageRangeText|. The ranges are @@ -105,23 +109,23 @@ function pageRangeTextToPageRanges(pageRangeText, opt_totalPageCount) { return []; } - var MAX_PAGE_NUMBER = 1000000000; - var totalPageCount = + const MAX_PAGE_NUMBER = 1000000000; + const totalPageCount = opt_totalPageCount ? opt_totalPageCount : MAX_PAGE_NUMBER; - var regex = /^\s*([0-9]*)\s*-\s*([0-9]*)\s*$/; - var parts = pageRangeText.split(/,/); + const regex = /^\s*([0-9]*)\s*-\s*([0-9]*)\s*$/; + const parts = pageRangeText.split(/,/); - var pageRanges = []; - for (var i = 0; i < parts.length; ++i) { - var match = parts[i].match(regex); + const pageRanges = []; + for (let i = 0; i < parts.length; ++i) { + const match = parts[i].match(regex); if (match) { if (!isPositiveInteger(match[1]) && match[1] !== '') return PageRangeStatus.SYNTAX_ERROR; if (!isPositiveInteger(match[2]) && match[2] !== '') return PageRangeStatus.SYNTAX_ERROR; - var from = match[1] ? parseInt(match[1], 10) : 1; - var to = match[2] ? parseInt(match[2], 10) : totalPageCount; + const from = match[1] ? parseInt(match[1], 10) : 1; + const to = match[2] ? parseInt(match[2], 10) : totalPageCount; if (from > to) return PageRangeStatus.SYNTAX_ERROR; if (to > totalPageCount) @@ -130,7 +134,7 @@ function pageRangeTextToPageRanges(pageRangeText, opt_totalPageCount) { } else { if (!isPositiveInteger(parts[i])) return PageRangeStatus.SYNTAX_ERROR; - var singlePageNumber = parseInt(parts[i], 10); + const singlePageNumber = parseInt(parts[i], 10); if (singlePageNumber > totalPageCount) return PageRangeStatus.LIMIT_ERROR; pageRanges.push({'from': singlePageNumber, 'to': singlePageNumber}); @@ -150,18 +154,18 @@ function pageRangeTextToPageRanges(pageRangeText, opt_totalPageCount) { * @return {!Array<number>} A list of all pages. */ function pageRangeTextToPageList(pageRangeText, totalPageCount) { - var pageRanges = pageRangeTextToPageRanges(pageRangeText, totalPageCount); - var pageList = []; + const pageRanges = pageRangeTextToPageRanges(pageRangeText, totalPageCount); + const pageList = []; if (Array.isArray(pageRanges)) { - for (var i = 0; i < pageRanges.length; ++i) { - for (var j = pageRanges[i].from; + for (let i = 0; i < pageRanges.length; ++i) { + for (let j = pageRanges[i].from; j <= Math.min(pageRanges[i].to, totalPageCount); ++j) { pageList.push(j); } } } if (pageList.length == 0) { - for (var j = 1; j <= totalPageCount; ++j) + for (let j = 1; j <= totalPageCount; ++j) pageList.push(j); } return pageList; @@ -173,7 +177,7 @@ function pageRangeTextToPageList(pageRangeText, totalPageCount) { * without any duplicates. |pageList| is not affected. */ function pageListToPageSet(pageList) { - var pageSet = []; + let pageSet = []; if (pageList.length == 0) return pageSet; pageSet = pageList.slice(0); @@ -219,7 +223,7 @@ function arrayContains(array, item) { */ function getStringForLocale(localizedStrings, locale) { locale = locale.toLowerCase(); - for (var i = 0; i < localizedStrings.length; i++) { + for (let i = 0; i < localizedStrings.length; i++) { if (localizedStrings[i].locale.toLowerCase() == locale) return localizedStrings[i].value; } diff --git a/chromium/chrome/browser/resources/print_preview/search/cloud_destination_list.js b/chromium/chrome/browser/resources/print_preview/search/cloud_destination_list.js deleted file mode 100644 index 7f0bcdd353d..00000000000 --- a/chromium/chrome/browser/resources/print_preview/search/cloud_destination_list.js +++ /dev/null @@ -1,39 +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('print_preview', function() { - 'use strict'; - - /** - * Sub-class of a destination list that shows cloud-based destinations. - * @param {!cr.EventTarget} eventTarget Event target to pass to destination - * items for dispatching SELECT events. - * @constructor - * @extends {print_preview.DestinationList} - */ - function CloudDestinationList(eventTarget) { - print_preview.DestinationList.call( - this, eventTarget, loadTimeData.getString('cloudDestinationsTitle'), - loadTimeData.getString('manage')); - } - - CloudDestinationList.prototype = { - __proto__: print_preview.DestinationList.prototype, - - /** @override */ - updateDestinations: function(destinations) { - // Change the action link from "Manage..." to "Setup..." if user only has - // the Docs printer. - var docsId = print_preview.Destination.GooglePromotedId.DOCS; - this.setActionLinkTextInternal(loadTimeData.getString( - destinations.length == 1 && destinations[0].id == docsId ? - 'setupCloudPrinters' : - 'manage')); - print_preview.DestinationList.prototype.updateDestinations.call( - this, destinations); - } - }; - - return {CloudDestinationList: CloudDestinationList}; -}); diff --git a/chromium/chrome/browser/resources/print_preview/search/destination_list.js b/chromium/chrome/browser/resources/print_preview/search/destination_list.js index 9c1a2ef0144..c809f3e7c5c 100644 --- a/chromium/chrome/browser/resources/print_preview/search/destination_list.js +++ b/chromium/chrome/browser/resources/print_preview/search/destination_list.js @@ -143,7 +143,7 @@ cr.define('print_preview', function() { */ getEstimatedHeightInPixels: function(numItems) { numItems = Math.min(numItems, this.destinations_.length); - var headerHeight = + const headerHeight = this.getChildElement('.destination-list > header').offsetHeight; return headerHeight + (numItems > 0 ? numItems * DestinationList.HEIGHT_OF_ITEM_ : @@ -185,7 +185,7 @@ cr.define('print_preview', function() { this.cloneTemplateInternal('destination-list-template')); this.getChildElement('.title').textContent = this.title_; if (this.actionLinkLabel_) { - var actionLinkEl = this.getChildElement('.action-link'); + const actionLinkEl = this.getChildElement('.action-link'); actionLinkEl.textContent = this.actionLinkLabel_; setIsVisible(actionLinkEl, true); } @@ -260,7 +260,7 @@ cr.define('print_preview', function() { if (!this.query_) { this.renderDestinationsList_(this.destinations_); } else { - var filteredDests = this.destinations_.filter(function(destination) { + const filteredDests = this.destinations_.filter(function(destination) { return destination.matches(assert(this.query_)); }, this); this.renderDestinationsList_(filteredDests); @@ -279,7 +279,7 @@ cr.define('print_preview', function() { this.getChildElement('.no-destinations-message'), destinations.length == 0); setIsVisible(this.getChildElement('.destination-list > footer'), false); - var numItems = destinations.length; + let numItems = destinations.length; if (destinations.length > this.shortListSize_ && !this.isShowAll_) { numItems = this.shortListSize_ - 1; this.getChildElement('.total').textContent = @@ -288,32 +288,33 @@ cr.define('print_preview', function() { } // Remove obsolete list items (those with no corresponding destinations). this.listItems_ = this.listItems_.filter(item => { - var isValid = this.destinationIds_.hasOwnProperty(item.destination.id); + const isValid = + this.destinationIds_.hasOwnProperty(item.destination.id); if (!isValid) this.removeChild(item); return isValid; }); // Prepare id -> list item cache for visible destinations. - var visibleListItems = {}; - for (var i = 0; i < numItems; i++) + const visibleListItems = {}; + for (let i = 0; i < numItems; i++) visibleListItems[destinations[i].id] = null; // Update visibility for all existing list items. this.listItems_.forEach(function(item) { - var isVisible = visibleListItems.hasOwnProperty(item.destination.id); + const isVisible = visibleListItems.hasOwnProperty(item.destination.id); setIsVisible(item.getElement(), isVisible); if (isVisible) visibleListItems[item.destination.id] = item; }); // Update the existing items, add the new ones (preserve the focused one). - var listEl = this.getChildElement('.destination-list > ul'); + const listEl = this.getChildElement('.destination-list > ul'); // We need to use activeElement instead of :focus selector, which doesn't // work in an inactive page. See crbug.com/723579. - var focusedEl = listEl.contains(document.activeElement) ? + const focusedEl = listEl.contains(document.activeElement) ? document.activeElement : null; - for (var i = 0; i < numItems; i++) { - var destination = assert(destinations[i]); - var listItem = visibleListItems[destination.id]; + for (let i = 0; i < numItems; i++) { + const destination = assert(destinations[i]); + const listItem = visibleListItems[destination.id]; if (listItem) { // Destination ID is the same, but it can be registered to a different // user account, hence passing it to the item update. @@ -334,9 +335,9 @@ cr.define('print_preview', function() { updateListItem_: function(listEl, listItem, focusedEl, destination) { listItem.update(destination, this.query_); - var itemEl = listItem.getElement(); + const itemEl = listItem.getElement(); // Preserve focused inner element, if there's one. - var focusedInnerEl = + const focusedInnerEl = focusedEl && itemEl.contains(focusedEl) ? focusedEl : null; if (focusedEl) itemEl.classList.add('moving'); @@ -356,7 +357,7 @@ cr.define('print_preview', function() { * @private */ renderListItem_: function(listEl, destination) { - var listItem = new print_preview.DestinationListItem( + const listItem = new print_preview.DestinationListItem( this.eventTarget_, destination, this.query_); this.addChild(listItem); listItem.render(assert(listEl)); 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 b52b4938cbd..f85e0af253a 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 @@ -138,12 +138,12 @@ cr.define('print_preview', function() { * @private */ updateUi_: function() { - var iconImg = this.getChildElement('.destination-list-item-icon'); + const 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; + const nameEl = this.getChildElement('.destination-list-item-name'); + let textContent = this.destination_.displayName; if (this.query_) { nameEl.textContent = ''; // When search query is specified, make it obvious why this particular @@ -153,7 +153,7 @@ cr.define('print_preview', function() { // Show the first matching property. this.destination_.extraPropertiesToMatch.some(function(property) { if (property.match(this.query_)) { - var hintSpan = document.createElement('span'); + const hintSpan = document.createElement('span'); hintSpan.className = 'search-hint'; nameEl.appendChild(hintSpan); this.addTextWithHighlight_(hintSpan, property); @@ -169,8 +169,8 @@ cr.define('print_preview', function() { nameEl.title = textContent; if (this.destination_.isExtension) { - var extensionNameEl = this.getChildElement('.extension-name'); - var extensionName = this.destination_.extensionName; + const extensionNameEl = this.getChildElement('.extension-name'); + const extensionName = this.destination_.extensionName; extensionNameEl.title = this.destination_.extensionName; if (this.query_) { extensionNameEl.textContent = ''; @@ -179,7 +179,7 @@ cr.define('print_preview', function() { extensionNameEl.textContent = this.destination_.extensionName; } - var extensionIconEl = this.getChildElement('.extension-icon'); + const extensionIconEl = this.getChildElement('.extension-icon'); extensionIconEl.style.backgroundImage = '-webkit-image-set(' + 'url(chrome://extension-icon/' + this.destination_.extensionId + '/24/1) 1x,' + @@ -192,13 +192,13 @@ cr.define('print_preview', function() { this.onExtensionIconKeyDown_.bind(this)); } - var extensionIndicatorEl = + const extensionIndicatorEl = this.getChildElement('.extension-controlled-indicator'); setIsVisible(extensionIndicatorEl, this.destination_.isExtension); // Initialize the element which renders the destination's offline status. this.getElement().classList.toggle('stale', this.destination_.isOffline); - var offlineStatusEl = this.getChildElement('.offline-status'); + const offlineStatusEl = this.getChildElement('.offline-status'); offlineStatusEl.textContent = this.destination_.offlineStatusText; setIsVisible(offlineStatusEl, this.destination_.isOffline); @@ -223,12 +223,12 @@ cr.define('print_preview', function() { * @private */ addTextWithHighlight_: function(parent, text) { - var sections = text.split(this.query_); - for (var i = 0; i < sections.length; ++i) { + const sections = text.split(this.query_); + for (let i = 0; i < sections.length; ++i) { if (i % 2 == 0) { parent.appendChild(document.createTextNode(sections[i])); } else { - var span = document.createElement('span'); + const span = document.createElement('span'); span.className = 'destination-list-item-query-highlight'; span.textContent = sections[i]; parent.appendChild(span); @@ -261,7 +261,7 @@ cr.define('print_preview', function() { // Check if the printer needs configuration before using. The user is only // allowed to set up one printer at one time. - var configureEvent = new CustomEvent( + const configureEvent = new CustomEvent( DestinationListItem.EventType.CONFIGURE_REQUEST, {detail: {destination: this.destination_}}); this.eventTarget_.dispatchEvent(configureEvent); @@ -275,7 +275,7 @@ cr.define('print_preview', function() { onDestinationActivated_: function() { if (this.destination_.connectionStatus != print_preview.DestinationConnectionStatus.UNREGISTERED) { - var selectEvt = new Event(DestinationListItem.EventType.SELECT); + const selectEvt = new Event(DestinationListItem.EventType.SELECT); selectEvt.destination = this.destination_; this.eventTarget_.dispatchEvent(selectEvt); } @@ -290,7 +290,7 @@ cr.define('print_preview', function() { onKeyDown_: function(e) { if (!hasKeyModifiers(e)) { if (e.keyCode == 13) { - var activeElementTag = document.activeElement ? + const activeElementTag = document.activeElement ? document.activeElement.tagName.toUpperCase() : ''; if (activeElementTag == 'LI') { @@ -307,7 +307,7 @@ cr.define('print_preview', function() { * @private */ onRegisterPromoClicked_: function() { - var promoClickedEvent = + const promoClickedEvent = new Event(DestinationListItem.EventType.REGISTER_PROMO_CLICKED); promoClickedEvent.destination = this.destination_; this.eventTarget_.dispatchEvent(promoClickedEvent); diff --git a/chromium/chrome/browser/resources/print_preview/search/destination_search.html b/chromium/chrome/browser/resources/print_preview/search/destination_search.html index 8be4042ddb0..7d3c4c325e0 100644 --- a/chromium/chrome/browser/resources/print_preview/search/destination_search.html +++ b/chromium/chrome/browser/resources/print_preview/search/destination_search.html @@ -9,8 +9,7 @@ <div class="search-box-container"></div> <div class="lists"> <div class="recent-list"></div> - <div class="local-list"></div> - <div class="cloud-list" hidden></div> + <div class="print-list"></div> </div> <div class="action-area"> <div class="button-strip"> diff --git a/chromium/chrome/browser/resources/print_preview/search/destination_search.js b/chromium/chrome/browser/resources/print_preview/search/destination_search.js index 7876776fc9a..8aa3456d3cb 100644 --- a/chromium/chrome/browser/resources/print_preview/search/destination_search.js +++ b/chromium/chrome/browser/resources/print_preview/search/destination_search.js @@ -41,12 +41,6 @@ cr.define('print_preview', function() { this.userInfo_ = userInfo; /** - * Instance of native layer used to send metrics to C++ metrics handler. - * @private {!print_preview.NativeLayer} - */ - this.nativeLayer_ = print_preview.NativeLayer.getInstance(); - - /** * Currently displayed printer sharing invitation. * @private {print_preview.Invitation} */ @@ -95,22 +89,13 @@ cr.define('print_preview', function() { this.addChild(this.recentList_); /** - * Destination list containing local destinations. - * @private {!print_preview.DestinationList} - */ - this.localList_ = new print_preview.DestinationList( - this, loadTimeData.getString('localDestinationsTitle'), - loadTimeData.getBoolean('showLocalManageButton') ? - loadTimeData.getString('manage') : - null); - this.addChild(this.localList_); - - /** - * Destination list containing cloud destinations. + * Destination list containing all print destinations. * @private {!print_preview.DestinationList} */ - this.cloudList_ = new print_preview.CloudDestinationList(this); - this.addChild(this.cloudList_); + this.printList_ = new print_preview.DestinationList( + this, loadTimeData.getString('printDestinationsTitle'), + loadTimeData.getString('manage')); + this.addChild(this.printList_); } /** @@ -121,13 +106,9 @@ cr.define('print_preview', function() { // Dispatched when user requests to sign-in into another Google account. ADD_ACCOUNT: 'print_preview.DestinationSearch.ADD_ACCOUNT', - // Dispatched when the user requests to manage their cloud destinations. - MANAGE_CLOUD_DESTINATIONS: - 'print_preview.DestinationSearch.MANAGE_CLOUD_DESTINATIONS', - - // Dispatched when the user requests to manage their local destinations. - MANAGE_LOCAL_DESTINATIONS: - 'print_preview.DestinationSearch.MANAGE_LOCAL_DESTINATIONS', + // Dispatched when the user requests to manage their print destinations. + MANAGE_PRINT_DESTINATIONS: + 'print_preview.DestinationSearch.MANAGE_PRINT_DESTINATIONS', // Dispatched when the user requests to sign-in to their Google account. SIGN_IN: 'print_preview.DestinationSearch.SIGN_IN' @@ -151,7 +132,6 @@ cr.define('print_preview', function() { if (getIsVisible(this.getChildElement('.cloudprint-promo'))) { this.metrics_.record( print_preview.Metrics.DestinationSearchBucket.SIGNIN_PROMPT); - this.nativeLayer_.recordAction('Signin_Impression_FromCloudPrint'); } if (this.userInfo_.initialized) this.onUsersChanged_(); @@ -163,8 +143,7 @@ cr.define('print_preview', function() { this.invitationStore_.startLoadingInvitations(); } else { // Collapse all destination lists - this.localList_.setIsShowAll(false); - this.cloudList_.setIsShowAll(false); + this.printList_.setIsShowAll(false); if (this.provisionalDestinationResolver_) this.provisionalDestinationResolver_.cancel(); this.resetSearch_(); @@ -179,11 +158,14 @@ cr.define('print_preview', function() { /** Shows the Google Cloud Print promotion banner. */ showCloudPrintPromo: function() { - setIsVisible(this.getChildElement('.cloudprint-promo'), true); + const cloudPrintPromoElement = this.getChildElement('.cloudprint-promo'); + if (getIsVisible(cloudPrintPromoElement)) + return; + + setIsVisible(cloudPrintPromoElement, true); if (this.getIsVisible()) { this.metrics_.record( print_preview.Metrics.DestinationSearchBucket.SIGNIN_PROMPT); - this.nativeLayer_.recordAction('Signin_Impression_FromCloudPrint'); } this.reflowLists_(); }, @@ -259,13 +241,9 @@ cr.define('print_preview', function() { this.updateInvitations_.bind(this)); this.tracker.add( - this.localList_, + this.printList_, print_preview.DestinationList.EventType.ACTION_LINK_ACTIVATED, - this.onManageLocalDestinationsActivated_.bind(this)); - this.tracker.add( - this.cloudList_, - print_preview.DestinationList.EventType.ACTION_LINK_ACTIVATED, - this.onManageCloudDestinationsActivated_.bind(this)); + this.onManagePrintDestinationsActivated_.bind(this)); this.tracker.add( this.userInfo_, print_preview.UserInfo.EventType.USERS_CHANGED, @@ -287,8 +265,7 @@ cr.define('print_preview', function() { decorateInternal: function() { this.searchBox_.render(this.getChildElement('.search-box-container')); this.recentList_.render(this.getChildElement('.recent-list')); - this.localList_.render(this.getChildElement('.local-list')); - this.cloudList_.render(this.getChildElement('.cloud-list')); + this.printList_.render(this.getChildElement('.print-list')); this.getChildElement('.promo-text').innerHTML = loadTimeData.getStringF( 'cloudPrintPromotion', '<a is="action-link" class="sign-in">', '</a>'); @@ -301,7 +278,7 @@ cr.define('print_preview', function() { * @private */ getAvailableListsHeight_: function() { - var elStyle = window.getComputedStyle(this.getElement()); + const elStyle = window.getComputedStyle(this.getElement()); return this.getElement().offsetHeight - parseInt(elStyle.getPropertyValue('padding-top'), 10) - parseInt(elStyle.getPropertyValue('padding-bottom'), 10) - @@ -318,8 +295,7 @@ cr.define('print_preview', function() { */ filterLists_: function(query) { this.recentList_.updateSearchQuery(query); - this.localList_.updateSearchQuery(query); - this.cloudList_.updateSearchQuery(query); + this.printList_.updateSearchQuery(query); }, /** @@ -336,17 +312,15 @@ cr.define('print_preview', function() { * @private */ renderDestinations_: function() { - var recentDestinations = []; - var localDestinations = []; - var cloudDestinations = []; - var unregisteredCloudDestinations = []; + const recentDestinations = this.destinationStore_.getRecentDestinations( + this.userInfo_.activeUser); + const localDestinations = []; + const cloudDestinations = []; + const unregisteredCloudDestinations = []; - var destinations = + const destinations = this.destinationStore_.destinations(this.userInfo_.activeUser); destinations.forEach(function(destination) { - if (destination.isRecent) { - recentDestinations.push(destination); - } if (destination.isLocal || destination.origin == print_preview.DestinationOrigin.DEVICE) { localDestinations.push(destination); @@ -367,17 +341,18 @@ cr.define('print_preview', function() { this.registerPromoShownMetricRecorded_ = true; } - var finalCloudDestinations = + const finalCloudDestinations = unregisteredCloudDestinations .slice(0, DestinationSearch.MAX_PROMOTED_UNREGISTERED_PRINTERS_) .concat( cloudDestinations, unregisteredCloudDestinations.slice( DestinationSearch.MAX_PROMOTED_UNREGISTERED_PRINTERS_)); + const finalPrintDestinations = + localDestinations.concat(finalCloudDestinations); this.recentList_.updateDestinations(recentDestinations); - this.localList_.updateDestinations(localDestinations); - this.cloudList_.updateDestinations(finalCloudDestinations); + this.printList_.updateDestinations(finalPrintDestinations); }, /** @@ -389,42 +364,39 @@ cr.define('print_preview', function() { return; } - var hasCloudList = getIsVisible(this.getChildElement('.cloud-list')); - var lists = [this.recentList_, this.localList_]; - if (hasCloudList) { - lists.push(this.cloudList_); - } - - var getListsTotalHeight = function(lists, counts) { + const lists = [this.recentList_, this.printList_]; + const getListsTotalHeight = function(lists, counts) { return lists.reduce(function(sum, list, index) { - var container = list.getContainerElement(); + const container = list.getContainerElement(); return sum + list.getEstimatedHeightInPixels(counts[index]) + parseInt(window.getComputedStyle(container).paddingBottom, 10); }, 0); }; - var getCounts = function(lists, count) { + const getCounts = function(lists, count) { return lists.map(function(list) { return count; }); }; - var availableHeight = this.getAvailableListsHeight_(); - var listsEl = this.getChildElement('.lists'); + const availableHeight = this.getAvailableListsHeight_(); + const listsEl = this.getChildElement('.lists'); listsEl.style.maxHeight = availableHeight + 'px'; - var maxListLength = lists.reduce(function(prevCount, list) { + const maxListLength = lists.reduce(function(prevCount, list) { return Math.max(prevCount, list.getDestinationsCount()); }, 0); - for (var i = 1; i <= maxListLength; i++) { + + let i = 1; + for (; i <= maxListLength; i++) { if (getListsTotalHeight(lists, getCounts(lists, i)) > availableHeight) { i--; break; } } - var counts = getCounts(lists, i); + const counts = getCounts(lists, i); // Fill up the possible n-1 free slots left by the previous loop. if (getListsTotalHeight(lists, counts) < availableHeight) { - for (var countIndex = 0; countIndex < counts.length; countIndex++) { + for (let countIndex = 0; countIndex < counts.length; countIndex++) { counts[countIndex]++; if (getListsTotalHeight(lists, counts) > availableHeight) { counts[countIndex]--; @@ -439,10 +411,10 @@ cr.define('print_preview', function() { // Set height of the list manually so that search filter doesn't change // lists height. - var listsHeight = getListsTotalHeight(lists, counts) + 'px'; + const listsHeight = getListsTotalHeight(lists, counts) + 'px'; if (listsHeight != listsEl.style.height) { // Try to close account select if there's a possibility it's open now. - var accountSelectEl = this.getChildElement('.account-select'); + const accountSelectEl = this.getChildElement('.account-select'); if (!accountSelectEl.disabled) { accountSelectEl.disabled = true; accountSelectEl.disabled = false; @@ -457,13 +429,10 @@ cr.define('print_preview', function() { * @private */ updateThrobbers_: function() { - this.localList_.setIsThrobberVisible( - this.destinationStore_.isLocalDestinationSearchInProgress); - this.cloudList_.setIsThrobberVisible( - this.destinationStore_.isCloudDestinationSearchInProgress); + this.printList_.setIsThrobberVisible( + this.destinationStore_.isPrintDestinationSearchInProgress); this.recentList_.setIsThrobberVisible( - this.destinationStore_.isLocalDestinationSearchInProgress && - this.destinationStore_.isCloudDestinationSearchInProgress); + this.destinationStore_.isPrintDestinationSearchInProgress); this.reflowLists_(); }, @@ -472,7 +441,7 @@ cr.define('print_preview', function() { * @private */ updateInvitations_: function() { - var invitations = this.userInfo_.activeUser ? + const invitations = this.userInfo_.activeUser ? this.invitationStore_.invitations(this.userInfo_.activeUser) : []; if (invitations.length > 0) { @@ -495,7 +464,7 @@ cr.define('print_preview', function() { * @private */ showInvitation_: function(invitation) { - var invitationText = ''; + let invitationText = ''; if (invitation.asGroupManager) { invitationText = loadTimeData.getStringF( 'groupPrinterSharingInviteText', HTMLEscape(invitation.sender), @@ -508,7 +477,7 @@ cr.define('print_preview', function() { } this.getChildElement('.invitation-text').innerHTML = invitationText; - var acceptButton = this.getChildElement('.invitation-accept-button'); + const acceptButton = this.getChildElement('.invitation-accept-button'); acceptButton.textContent = loadTimeData.getString( invitation.asGroupManager ? 'acceptForGroup' : 'accept'); acceptButton.disabled = !!this.invitationStore_.invitationInProgress; @@ -524,17 +493,17 @@ cr.define('print_preview', function() { * @private */ onUsersChanged_: function() { - var loggedIn = this.userInfo_.loggedIn; + const loggedIn = this.userInfo_.loggedIn; if (loggedIn) { - var accountSelectEl = this.getChildElement('.account-select'); + const accountSelectEl = this.getChildElement('.account-select'); accountSelectEl.innerHTML = ''; this.userInfo_.users.forEach(function(account) { - var option = document.createElement('option'); + const option = document.createElement('option'); option.text = account; option.value = account; accountSelectEl.add(option); }); - var option = document.createElement('option'); + const option = document.createElement('option'); option.text = loadTimeData.getString('addAccountTitle'); option.value = ''; accountSelectEl.add(option); @@ -545,7 +514,6 @@ cr.define('print_preview', function() { } setIsVisible(this.getChildElement('.user-info'), loggedIn); - setIsVisible(this.getChildElement('.cloud-list'), loggedIn); setIsVisible(this.getChildElement('.cloudprint-promo'), !loggedIn); this.updateInvitations_(); }, @@ -569,15 +537,9 @@ cr.define('print_preview', function() { * @private */ onDestinationConfigureRequest_: function(event) { - var destination = event.detail.destination; - // Cloud Print Device printers are stored in the local list - // crbug.com/713831. - // TODO(crbug.com/416701): Upon resolution, update this. - var destinationItem = - (destination.isLocal || - destination.origin == print_preview.DestinationOrigin.DEVICE) ? - this.localList_.getDestinationItem(destination.id) : - this.cloudList_.getDestinationItem(destination.id); + const destination = event.detail.destination; + const destinationItem = + this.printList_.getDestinationItem(destination.id); assert( destinationItem != null, 'User does not select a valid destination item.'); @@ -610,12 +572,12 @@ cr.define('print_preview', function() { .then( response => { this.destinationInConfiguring_ = null; - this.localList_.getDestinationItem(destination.id) + this.printList_.getDestinationItem(destination.id) .onConfigureResolved(response); }, () => { this.destinationInConfiguring_ = null; - this.localList_.getDestinationItem(destination.id) + this.printList_.getDestinationItem(destination.id) .onConfigureResolved( {printerId: destination.id, success: false}); }); @@ -650,7 +612,7 @@ cr.define('print_preview', function() { !!this.provisionalDestinationResolver_, 'Unable to create provisional destination resolver'); - var lastFocusedElement = document.activeElement; + const lastFocusedElement = document.activeElement; this.addChild(this.provisionalDestinationResolver_); this.provisionalDestinationResolver_.run(this.getElement()) .then(resolvedDestination => { @@ -688,14 +650,8 @@ cr.define('print_preview', function() { * @private */ onDestinationStoreSelect_: function() { - var destinations = - this.destinationStore_.destinations(this.userInfo_.activeUser); - var recentDestinations = []; - destinations.forEach(function(destination) { - if (destination.isRecent) { - recentDestinations.push(destination); - } - }); + const recentDestinations = this.destinationStore_.getRecentDestinations( + this.userInfo_.activeUser); this.recentList_.updateDestinations(recentDestinations); this.reflowLists_(); }, @@ -725,23 +681,13 @@ cr.define('print_preview', function() { }, /** - * Called when the manage cloud printers action is activated. - * @private - */ - onManageCloudDestinationsActivated_: function() { - cr.dispatchSimpleEvent( - this, - print_preview.DestinationSearch.EventType.MANAGE_CLOUD_DESTINATIONS); - }, - - /** - * Called when the manage local printers action is activated. + * Called when the manage all printers action is activated. * @private */ - onManageLocalDestinationsActivated_: function() { + onManagePrintDestinationsActivated_: function() { cr.dispatchSimpleEvent( this, - print_preview.DestinationSearch.EventType.MANAGE_LOCAL_DESTINATIONS); + print_preview.DestinationSearch.EventType.MANAGE_PRINT_DESTINATIONS); }, /** @@ -761,8 +707,8 @@ cr.define('print_preview', function() { * @private */ onAccountChange_: function() { - var accountSelectEl = this.getChildElement('.account-select'); - var account = + const accountSelectEl = this.getChildElement('.account-select'); + const account = accountSelectEl.options[accountSelectEl.selectedIndex].value; if (account) { this.userInfo_.activeUser = account; @@ -773,7 +719,7 @@ cr.define('print_preview', function() { } else { cr.dispatchSimpleEvent(this, DestinationSearch.EventType.ADD_ACCOUNT); // Set selection back to the active user. - for (var i = 0; i < accountSelectEl.options.length; i++) { + for (let i = 0; i < accountSelectEl.options.length; i++) { if (accountSelectEl.options[i].value == this.userInfo_.activeUser) { accountSelectEl.selectedIndex = i; break; diff --git a/chromium/chrome/browser/resources/print_preview/search/provisional_destination_resolver.js b/chromium/chrome/browser/resources/print_preview/search/provisional_destination_resolver.js index 29dc741bcee..e60eecccf15 100644 --- a/chromium/chrome/browser/resources/print_preview/search/provisional_destination_resolver.js +++ b/chromium/chrome/browser/resources/print_preview/search/provisional_destination_resolver.js @@ -111,11 +111,11 @@ cr.define('print_preview', function() { this.setElementInternal( this.cloneTemplateInternal('extension-usb-resolver')); - var extNameEl = this.getChildElement('.usb-permission-extension-name'); + const extNameEl = this.getChildElement('.usb-permission-extension-name'); extNameEl.title = this.destination_.extensionName; extNameEl.textContent = this.destination_.extensionName; - var extIconEl = this.getChildElement('.usb-permission-extension-icon'); + const extIconEl = this.getChildElement('.usb-permission-extension-icon'); extIconEl.style.backgroundImage = '-webkit-image-set(' + 'url(chrome://extension-icon/' + this.destination_.extensionId + '/24/1) 1x,' + diff --git a/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings.js b/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings.js index 799f206980a..601da25b01c 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings.js @@ -107,7 +107,7 @@ cr.define('print_preview', function() { /** @override */ onEnterPressedInternal: function() { - var doneButton = this.getChildElement('.button-strip .done-button'); + const doneButton = this.getChildElement('.button-strip .done-button'); if (!doneButton.disabled) doneButton.click(); return !doneButton.disabled; @@ -118,7 +118,7 @@ cr.define('print_preview', function() { * @private */ getAvailableContentHeight_: function() { - var elStyle = window.getComputedStyle(this.getElement()); + const elStyle = window.getComputedStyle(this.getElement()); return this.getElement().offsetHeight - parseInt(elStyle.getPropertyValue('padding-top'), 10) - parseInt(elStyle.getPropertyValue('padding-bottom'), 10) - @@ -132,8 +132,8 @@ cr.define('print_preview', function() { * @private */ filterLists_: function(query) { - var atLeastOneMatch = false; - var lastVisibleItemWithBubble = null; + let atLeastOneMatch = false; + let lastVisibleItemWithBubble = null; this.items_.forEach(function(item) { item.updateSearchQuery(query); if (getIsVisible(item.getElement())) @@ -168,29 +168,29 @@ cr.define('print_preview', function() { }); this.items_ = []; - var extraPadding = this.element_.querySelector( + let extraPadding = this.element_.querySelector( '.' + AdvancedSettings.Classes_.EXTRA_PADDING); if (extraPadding) extraPadding.parentNode.removeChild(extraPadding); - var vendorCapabilities = this.printTicketStore_.vendorItems.capability; + const vendorCapabilities = this.printTicketStore_.vendorItems.capability; if (!vendorCapabilities) return; - var availableHeight = this.getAvailableContentHeight_(); - var containerEl = this.getChildElement('.settings-area'); + const availableHeight = this.getAvailableContentHeight_(); + const containerEl = this.getChildElement('.settings-area'); containerEl.style.maxHeight = availableHeight + 'px'; - var settingsEl = this.getChildElement('.settings'); + const settingsEl = this.getChildElement('.settings'); vendorCapabilities.forEach(capability => { - var item = new print_preview.AdvancedSettingsItem( + const item = new print_preview.AdvancedSettingsItem( this.printTicketStore_, capability); this.addChild(item); item.render(settingsEl); this.items_.push(item); }); - var searchBoxArea = this.getChildElement('.search-box-area'); + const searchBoxArea = this.getChildElement('.search-box-area'); if (this.items_.length <= 1) { setIsVisible(searchBoxArea, false); } else { @@ -221,7 +221,7 @@ cr.define('print_preview', function() { onApplySettings_: function(evt) { this.setIsVisible(false); - var values = {}; + const values = {}; this.items_.forEach(item => { if (item.isModified()) values[item.id] = item.selectedValue; diff --git a/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings_item.js b/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings_item.js index 40e4be31452..b5ce64999e5 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings_item.js +++ b/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings_item.js @@ -155,7 +155,7 @@ cr.define('print_preview', function() { this.selectedValue_ = this.text_.value || null; if (this.query_) { - var optionMatches = (this.selectedValue_ || '').match(this.query_); + const optionMatches = (this.selectedValue_ || '').match(this.query_); // Even if there's no match anymore, keep the item visible to do not // surprise user. Even if there's a match, do not show the bubble, user // is already aware that this option is visible and matches the search. @@ -173,7 +173,7 @@ cr.define('print_preview', function() { * @private */ getEntityDisplayName_: function(entity) { - var displayName = entity.display_name; + let displayName = entity.display_name; if (!displayName && entity.display_name_localized) displayName = getStringForCurrentLocale(entity.display_name_localized); return displayName || ''; @@ -184,22 +184,22 @@ cr.define('print_preview', function() { * @private */ renderCapability_: function() { - var textContent = this.getEntityDisplayName_(this.capability_); + const textContent = this.getEntityDisplayName_(this.capability_); // Whether capability name matches the query. - var nameMatches = this.query_ ? !!textContent.match(this.query_) : true; + const nameMatches = this.query_ ? !!textContent.match(this.query_) : true; // An array of text segments of the capability value matching the query. - var optionMatches = null; + let optionMatches = null; if (this.query_) { if (this.capability_.type == 'SELECT') { // Look for the first option that matches the query. - for (var i = 0; i < this.select_.length && !optionMatches; i++) + for (let i = 0; i < this.select_.length && !optionMatches; i++) optionMatches = this.select_.options[i].text.match(this.query_); } else { optionMatches = (this.text_.value || this.text_.placeholder || '').match(this.query_); } } - var matches = nameMatches || !!optionMatches; + const matches = nameMatches || !!optionMatches; if (!optionMatches) this.hideSearchBubble_(); @@ -208,7 +208,7 @@ cr.define('print_preview', function() { if (!matches) return; - var nameEl = this.getChildElement('.advanced-settings-item-label'); + const nameEl = this.getChildElement('.advanced-settings-item-label'); if (this.query_) { nameEl.textContent = ''; this.addTextWithHighlight_(nameEl, textContent); @@ -227,7 +227,7 @@ cr.define('print_preview', function() { * @private */ showSearchBubble_: function(text) { - var element = + const element = this.capability_.type == 'SELECT' ? this.select_ : this.text_; if (!this.searchBubble_) { this.searchBubble_ = new print_preview.SearchBubble(text); @@ -270,17 +270,17 @@ cr.define('print_preview', function() { setIsVisible( assert(this.getChildElement('.advanced-settings-item-value-select')), true); - var selectEl = this.select_; - var indexToSelect = 0; + const selectEl = this.select_; + let indexToSelect = 0; this.capability_.select_cap.option.forEach(function(option, index) { - var item = document.createElement('option'); + const item = document.createElement('option'); item.text = this.getEntityDisplayName_(option); item.value = option.value; if (option.is_default) indexToSelect = index; selectEl.appendChild(item); }, this); - for (var i = 0, option; (option = selectEl.options[i]); i++) { + for (let i = 0, option; (option = selectEl.options[i]); i++) { if (option.value == this.selectedValue_) { indexToSelect = i; break; @@ -298,7 +298,7 @@ cr.define('print_preview', function() { assert(this.getChildElement('.advanced-settings-item-value-text')), true); - var defaultValue = null; + let defaultValue = null; if (this.capability_.type == 'TYPED_VALUE' && this.capability_.typed_value_cap) { defaultValue = this.capability_.typed_value_cap.default || null; @@ -324,7 +324,7 @@ cr.define('print_preview', function() { if (i % 2 == 0) { parent.appendChild(document.createTextNode(section)); } else { - var span = document.createElement('span'); + const span = document.createElement('span'); span.className = 'advanced-settings-item-query-highlight'; span.textContent = section; parent.appendChild(span); diff --git a/chromium/chrome/browser/resources/print_preview/settings/color_settings.js b/chromium/chrome/browser/resources/print_preview/settings/color_settings.js index c2df674dfc6..bb9e7d0eed9 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/color_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/color_settings.js @@ -58,8 +58,8 @@ cr.define('print_preview', function() { * @private */ onSelectChange_: function() { - var select = this.select_; - var isColor = select.options[select.selectedIndex].value == 'color'; + const select = this.select_; + const isColor = select.options[select.selectedIndex].value == 'color'; this.colorTicketItem_.updateValue(isColor); }, @@ -78,9 +78,9 @@ cr.define('print_preview', function() { */ updateState_: function() { if (this.isAvailable()) { - var select = this.select_; - var valueToSelect = this.colorTicketItem_.getValue() ? 'color' : 'bw'; - for (var i = 0; i < select.options.length; i++) { + const select = this.select_; + const valueToSelect = this.colorTicketItem_.getValue() ? 'color' : 'bw'; + for (let i = 0; i < select.options.length; i++) { if (select.options[i].value == valueToSelect) { select.selectedIndex = i; break; diff --git a/chromium/chrome/browser/resources/print_preview/settings/copies_settings.js b/chromium/chrome/browser/resources/print_preview/settings/copies_settings.js index 1dbcc74d6ba..0079fb529c6 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/copies_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/copies_settings.js @@ -139,7 +139,7 @@ cr.define('print_preview', function() { */ onTextfieldTimeout_: function() { this.textfieldTimeout_ = null; - var newValue = + const newValue = (this.inputField_.validity.valid && this.inputField_.value != '') ? this.inputField_.valueAsNumber.toString() : ''; 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 54d1994e9c8..631df0d33df 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/destination_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/destination_settings.js @@ -80,7 +80,7 @@ cr.define('print_preview', function() { /** @override */ set isEnabled(isEnabled) { - var changeButton = this.getElement().getElementsByClassName( + const changeButton = this.getElement().getElementsByClassName( print_preview.DestinationSettingsClasses_.CHANGE_BUTTON)[0]; changeButton.disabled = !isEnabled; }, @@ -88,7 +88,7 @@ cr.define('print_preview', function() { /** @override */ enterDocument: function() { print_preview.SettingsSection.prototype.enterDocument.call(this); - var changeButton = this.getElement().getElementsByClassName( + const changeButton = this.getElement().getElementsByClassName( print_preview.DestinationSettingsClasses_.CHANGE_BUTTON)[0]; this.tracker.add( changeButton, 'click', this.onChangeButtonClick_.bind(this)); @@ -118,34 +118,34 @@ cr.define('print_preview', function() { * @private */ onDestinationSelect_: function() { - var destinationSettingsBoxEl = + const destinationSettingsBoxEl = this.getChildElement('.destination-settings-box'); - var destination = this.destinationStore_.selectedDestination; + const destination = this.destinationStore_.selectedDestination; if (destination != null) { - var nameEl = this.getElement().getElementsByClassName( + const nameEl = this.getElement().getElementsByClassName( print_preview.DestinationSettingsClasses_.NAME)[0]; nameEl.textContent = destination.displayName; nameEl.title = destination.displayName; - var iconEl = this.getElement().getElementsByClassName( + const 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( + const hint = destination.hint; + const locationEl = this.getElement().getElementsByClassName( print_preview.DestinationSettingsClasses_.LOCATION)[0]; locationEl.textContent = hint; locationEl.title = hint; - var offlineStatusText = destination.offlineStatusText; - var offlineStatusEl = + const offlineStatusText = destination.offlineStatusText; + const offlineStatusEl = this.getChildElement('.destination-settings-offline-status'); offlineStatusEl.textContent = offlineStatusText; offlineStatusEl.title = offlineStatusText; - var isOffline = destination.isOffline; + const isOffline = destination.isOffline; destinationSettingsBoxEl.classList.toggle( print_preview.DestinationSettingsClasses_.STALE, isOffline); setIsVisible(locationEl, !isOffline); @@ -159,9 +159,9 @@ cr.define('print_preview', function() { }, onSelectedDestinationNameSet_: function() { - var destinationName = + const destinationName = this.destinationStore_.selectedDestination.displayName; - var nameEl = this.getElement().getElementsByClassName( + const nameEl = this.getElement().getElementsByClassName( print_preview.DestinationSettingsClasses_.THOBBER_NAME)[0]; nameEl.textContent = destinationName; nameEl.title = destinationName; diff --git a/chromium/chrome/browser/resources/print_preview/settings/dpi_settings.js b/chromium/chrome/browser/resources/print_preview/settings/dpi_settings.js index 4e44a50198e..4d2823d89bf 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/dpi_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/dpi_settings.js @@ -21,8 +21,8 @@ cr.define('print_preview', function() { /** @override */ getDefaultDisplayName_: function(option) { - var hDpi = option.horizontal_dpi || 0; - var vDpi = option.vertical_dpi || 0; + const hDpi = option.horizontal_dpi || 0; + const vDpi = option.vertical_dpi || 0; if (hDpi > 0 && vDpi > 0 && hDpi != vDpi) { return loadTimeData.getStringF( 'nonIsotropicDpiItemLabel', hDpi.toLocaleString(), diff --git a/chromium/chrome/browser/resources/print_preview/settings/layout_settings.js b/chromium/chrome/browser/resources/print_preview/settings/layout_settings.js index acb018e86f0..ec423d22f94 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/layout_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/layout_settings.js @@ -58,8 +58,8 @@ cr.define('print_preview', function() { * @private */ onSelectChange_: function() { - var select = this.select_; - var isLandscape = + const select = this.select_; + const isLandscape = select.options[select.selectedIndex].value == 'landscape'; this.landscapeTicketItem_.updateValue(isLandscape); }, @@ -81,10 +81,10 @@ cr.define('print_preview', function() { */ onLandscapeTicketItemChange_: function() { if (this.isAvailable()) { - var select = this.select_; - var valueToSelect = + const select = this.select_; + const valueToSelect = this.landscapeTicketItem_.getValue() ? 'landscape' : 'portrait'; - for (var i = 0; i < select.options.length; i++) { + for (let i = 0; i < select.options.length; i++) { if (select.options[i].value == valueToSelect) { select.selectedIndex = i; break; diff --git a/chromium/chrome/browser/resources/print_preview/settings/margin_settings.js b/chromium/chrome/browser/resources/print_preview/settings/margin_settings.js index f99b3de2fa8..4e8168d90a6 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/margin_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/margin_settings.js @@ -75,8 +75,8 @@ cr.define('print_preview', function() { * @private */ onSelectChange_: function() { - var select = this.select_; - var marginsType = + const select = this.select_; + const marginsType = /** @type {!print_preview.ticket_items.MarginsTypeValue} */ ( select.selectedIndex); this.marginsTypeTicketItem_.updateValue(marginsType); @@ -89,11 +89,11 @@ cr.define('print_preview', function() { */ onMarginsTypeTicketItemChange_: function() { if (this.isAvailable()) { - var select = this.select_; - var marginsType = + const select = this.select_; + const marginsType = /** @type {!print_preview.ticket_items.MarginsTypeValue} */ ( this.marginsTypeTicketItem_.getValue()); - var selectedMarginsType = + const selectedMarginsType = /** @type {!print_preview.ticket_items.MarginsTypeValue} */ ( select.selectedIndex); if (marginsType != selectedMarginsType) { diff --git a/chromium/chrome/browser/resources/print_preview/settings/more_settings.js b/chromium/chrome/browser/resources/print_preview/settings/more_settings.js index 3c044fd4efc..5648df8a97b 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/more_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/more_settings.js @@ -124,18 +124,18 @@ cr.define('print_preview', function() { this.getChildElement('.more-settings-label').textContent = loadTimeData.getString( this.isExpanded ? 'lessOptionsLabel' : 'moreOptionsLabel'); - var iconEl = this.getChildElement('.more-settings-icon'); + const iconEl = this.getChildElement('.more-settings-icon'); iconEl.classList.toggle('more-settings-icon-plus', !this.isExpanded); iconEl.classList.toggle('more-settings-icon-minus', this.isExpanded); - var availableSections = + const availableSections = this.settingsSections_.reduce(function(count, section) { return count + (section.isAvailable() ? 1 : 0); }, 0); // Magic 6 is chosen as the number of sections when it still feels like // manageable and not too crowded. - var hasSectionsToToggle = availableSections > 6 && + const hasSectionsToToggle = availableSections > 6 && this.settingsSections_.some(function(section) { return section.hasCollapsibleContent(); }); @@ -145,7 +145,7 @@ cr.define('print_preview', function() { else fadeOutElement(this.getElement()); - var collapseContent = !this.isExpanded && hasSectionsToToggle; + const collapseContent = !this.isExpanded && hasSectionsToToggle; this.settingsSections_.forEach(function(section) { section.setCollapseContent(collapseContent, noAnimation); }); diff --git a/chromium/chrome/browser/resources/print_preview/settings/other_options_settings.js b/chromium/chrome/browser/resources/print_preview/settings/other_options_settings.js index 56c10e26bd8..ec3d88b7a63 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/other_options_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/other_options_settings.js @@ -186,7 +186,7 @@ cr.define('print_preview', function() { * element, as this checkbox is enabled based on whether the user has * selected something in the page, which is different logic from the * other elements. */ - for (var i = 0; i < this.elements_.length - 1; i++) + for (let i = 0; i < this.elements_.length - 1; i++) this.elements_[i].checkbox.disabled = !isEnabled; }, @@ -207,13 +207,13 @@ cr.define('print_preview', function() { /** @override */ exitDocument: function() { print_preview.SettingsSection.prototype.exitDocument.call(this); - for (var i = 0; i < this.elements_.length; i++) + for (let i = 0; i < this.elements_.length; i++) this.elements_[i].exitDocument(); }, /** @override */ decorateInternal: function() { - for (var i = 0; i < this.elements_.length; i++) + for (let i = 0; i < this.elements_.length; i++) this.elements_[i].decorate(); $('rasterize-container').hidden = !this.rasterizeEnabled_; }, @@ -221,7 +221,7 @@ cr.define('print_preview', function() { /** @override */ updateUiStateInternal: function() { if (this.isAvailable()) { - for (var i = 0; i < this.elements_.length; i++) + for (let i = 0; i < this.elements_.length; i++) this.elements_[i].setVisibility(this.collapseContent); } print_preview.SettingsSection.prototype.updateUiStateInternal.call(this); 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 edbd64c3e23..da8d5db78dd 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/page_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/page_settings.js @@ -109,7 +109,7 @@ cr.define('print_preview', function() { /** @override */ enterDocument: function() { print_preview.SettingsSection.prototype.enterDocument.call(this); - var customInput = assert(this.customInput_); + const customInput = assert(this.customInput_); this.tracker.add( assert(this.allRadio_), 'click', this.onAllRadioClick_.bind(this)); this.tracker.add( @@ -164,7 +164,7 @@ cr.define('print_preview', function() { fadeOutElement(this.customHintEl_); return; } - var message; + let message; if (validity === PageRangeStatus.LIMIT_ERROR) { if (this.pageRangeTicketItem_.getDocumentNumPages()) { message = loadTimeData.getStringF( @@ -277,7 +277,7 @@ cr.define('print_preview', function() { */ onPageRangeTicketItemChange_: function() { if (this.isAvailable()) { - var pageRangeStr = this.pageRangeTicketItem_.getValue(); + const pageRangeStr = this.pageRangeTicketItem_.getValue(); if (pageRangeStr || this.customRadio_.checked) { if (!document.hasFocus() || document.activeElement != this.customInput_) { diff --git a/chromium/chrome/browser/resources/print_preview/settings/scaling_settings.js b/chromium/chrome/browser/resources/print_preview/settings/scaling_settings.js index 2b12f418253..a4174049a4c 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/scaling_settings.js +++ b/chromium/chrome/browser/resources/print_preview/settings/scaling_settings.js @@ -227,7 +227,7 @@ cr.define('print_preview', function() { return; // Convert value to a valid number or ''. The scaling ticket item assumes // the only invalid value is ''. - var value = + const value = (this.inputField_.validity.valid && this.inputField_.value != '') ? this.inputField_.valueAsNumber.toString() : ''; diff --git a/chromium/chrome/browser/resources/print_preview/settings/settings_section.js b/chromium/chrome/browser/resources/print_preview/settings/settings_section.js index 6deb0aa3393..e3b89b32e68 100644 --- a/chromium/chrome/browser/resources/print_preview/settings/settings_section.js +++ b/chromium/chrome/browser/resources/print_preview/settings/settings_section.js @@ -91,8 +91,9 @@ cr.define('print_preview', function() { * @protected */ updateUiStateInternal: function(opt_noAnimation) { - var hasCollapsibleContent = this.hasCollapsibleContent(); - var changed = this.hasCollapsibleContentCached_ != hasCollapsibleContent; + const hasCollapsibleContent = this.hasCollapsibleContent(); + const changed = + this.hasCollapsibleContentCached_ != hasCollapsibleContent; this.hasCollapsibleContentCached_ = hasCollapsibleContent; if (this.isSectionVisibleInternal()) 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 ab8b1d59fc0..fb20c2d7e06 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 @@ -69,22 +69,22 @@ cr.define('print_preview', function() { * @private */ updateSelect_: function() { - var select = this.select_; + const select = this.select_; if (!this.isAvailable()) { select.innerHTML = ''; return; } // Should the select content be updated? - var sameContent = + const sameContent = this.ticketItem_.capability.option.length == select.length && this.ticketItem_.capability.option.every(function(option, index) { return select.options[index].value == JSON.stringify(option); }); - var indexToSelect = select.selectedIndex; + let indexToSelect = select.selectedIndex; if (!sameContent) { select.innerHTML = ''; this.ticketItem_.capability.option.forEach(function(option, index) { - var selectOption = document.createElement('option'); + const selectOption = document.createElement('option'); selectOption.text = this.getCustomDisplayName_(option) || this.getDefaultDisplayName_(option); selectOption.value = JSON.stringify(option); @@ -94,8 +94,8 @@ cr.define('print_preview', function() { }, this); } // Try to select current ticket item. - var valueToSelect = JSON.stringify(this.ticketItem_.getValue()); - for (var i = 0, option; (option = select.options[i]); i++) { + const valueToSelect = JSON.stringify(this.ticketItem_.getValue()); + for (let i = 0, option; (option = select.options[i]); i++) { if (option.value == valueToSelect) { indexToSelect = i; break; @@ -111,7 +111,7 @@ cr.define('print_preview', function() { * @private */ getCustomDisplayName_: function(option) { - var displayName = option.custom_display_name; + let displayName = option.custom_display_name; if (!displayName && option.custom_display_name_localized) { displayName = getStringForCurrentLocale(option.custom_display_name_localized); @@ -133,7 +133,7 @@ cr.define('print_preview', function() { * @private */ onSelectChange_: function() { - var select = this.select_; + const select = this.select_; this.ticketItem_.updateValue( JSON.parse(select.options[select.selectedIndex].value)); }, 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 4fd285afc89..04a91c17835 100644 --- a/chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb +++ b/chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb @@ -8,8 +8,9 @@ ## ## Top level settings ## -version_id: 13 +version_id: 15 sampled_ping_probability: 0.01 +max_archived_binaries_to_report: 10 default_file_type { uma_value: 18 ping_setting: SAMPLED_PING @@ -786,6 +787,41 @@ file_types { uma_value: 23 ping_setting: FULL_PING } +file_types { + # Opened by uTorrent and Transmission (can be a renamed .torrent) + # Added crbug.com/767502 + extension: "btapp" + uma_value: 298 + ping_setting: FULL_PING +} +file_types { + # Opened by uTorrent and Transmission (can be a renamed .torrent) + # Added crbug.com/767502 + extension: "btbtskin" + uma_value: 299 + ping_setting: FULL_PING +} +file_types { + # Opened by uTorrent and Transmission (can be a renamed .torrent) + # Added crbug.com/767502 + extension: "btinstall" + uma_value: 300 + ping_setting: FULL_PING +} +file_types { + # Opened by uTorrent and Transmission (can be a renamed .torrent) + # Added crbug.com/767502 + extension: "btkey" + uma_value: 301 + ping_setting: FULL_PING +} +file_types { + # Opened by uTorrent and Transmission (can be a renamed .torrent) + # Added crbug.com/767502 + extension: "btsearch" + uma_value: 302 + ping_setting: FULL_PING +} ## ## Windows-specific files @@ -1529,7 +1565,7 @@ file_types { } } -# Other Windows files +# Other Windows files (some cross-platform too) file_types { extension: "ad" uma_value: 262 @@ -1695,6 +1731,30 @@ file_types { } } file_types { + # HTML-like file. This extension can be abused by UwS campaigns to evade + # referrer attribution via a two-level download scheme. crbug.com/719784 + # Added in https://crbug.com/762702 + extension: "dhtml" + uma_value: 303 + ping_setting: FULL_PING +} +file_types { + # HTML-like file. This extension can be abused by UwS campaigns to evade + # referrer attribution via a two-level download scheme. crbug.com/719784 + # Added in https://crbug.com/762702 + extension: "dhtm" + uma_value: 304 + ping_setting: FULL_PING +} +file_types { + # HTML-like file. This extension can be abused by UwS campaigns to evade + # referrer attribution via a two-level download scheme. crbug.com/719784 + # Added in https://crbug.com/762702 + extension: "dht" + uma_value: 305 + ping_setting: FULL_PING +} +file_types { # Windows executable. It can gain control of an executable launched from the # same directory, so it can do bad things without ever being clicked on. extension: "dll" @@ -1814,10 +1874,6 @@ file_types { extension: "htm" uma_value: 284 ping_setting: FULL_PING - platform_settings { - danger_level: NOT_DANGEROUS - auto_open_hint: ALLOW_AUTO_OPEN - } } file_types { # HTML file. This extension is abused by UwS campaigns to evade referrer @@ -1825,10 +1881,6 @@ file_types { extension: "html" uma_value: 285 ping_setting: FULL_PING - platform_settings { - danger_level: NOT_DANGEROUS - auto_open_hint: ALLOW_AUTO_OPEN - } } file_types { # Hypertext Template File. See https://support.microsoft.com/kb/181689. @@ -2131,6 +2183,30 @@ file_types { } } file_types { + # HTML-like file. This extension can be abused by UwS campaigns to evade + # referrer attribution via a two-level download scheme. crbug.com/719784 + # Added in https://crbug.com/762702 + extension: "shtml" + uma_value: 306 + ping_setting: FULL_PING +} +file_types { + # HTML-like file. This extension can be abused by UwS campaigns to evade + # referrer attribution via a two-level download scheme. crbug.com/719784 + # Added in https://crbug.com/762702 + extension: "shtm" + uma_value: 307 + ping_setting: FULL_PING +} +file_types { + # HTML-like file. This extension can be abused by UwS campaigns to evade + # referrer attribution via a two-level download scheme. crbug.com/719784 + # Added in https://crbug.com/762702 + extension: "sht" + uma_value: 308 + ping_setting: FULL_PING +} +file_types { # System executable. Windows tries hard to prevent you from opening these # types of files. extension: "sys" @@ -2154,6 +2230,112 @@ file_types { } } file_types { + # A Visio file type. Added in https://crbug.com/771469 + extension: "vdx" + uma_value: 289 + ping_setting: FULL_PING + platform_settings { + platform: PLATFORM_WINDOWS + danger_level: ALLOW_ON_USER_GESTURE + auto_open_hint: ALLOW_AUTO_OPEN + } +} +file_types { + # A Visio file type. Added in https://crbug.com/771469 + extension: "vsx" + uma_value: 290 + ping_setting: FULL_PING + platform_settings { + platform: PLATFORM_WINDOWS + danger_level: ALLOW_ON_USER_GESTURE + auto_open_hint: ALLOW_AUTO_OPEN + } +} +file_types { + # A Visio file type. Added in https://crbug.com/771469 + extension: "vtx" + uma_value: 291 + ping_setting: FULL_PING + platform_settings { + platform: PLATFORM_WINDOWS + danger_level: ALLOW_ON_USER_GESTURE + auto_open_hint: ALLOW_AUTO_OPEN + } +} +file_types { + # A Visio file type. Added in https://crbug.com/771469 + extension: "vsdx" + uma_value: 292 + ping_setting: FULL_PING + platform_settings { + platform: PLATFORM_WINDOWS + danger_level: ALLOW_ON_USER_GESTURE + auto_open_hint: ALLOW_AUTO_OPEN + } + # TODO(nparker): Unpack these as ZIP files. https://crbug.com/628796 +} +file_types { + # A Visio file type. Added in https://crbug.com/771469 + extension: "vssx" + uma_value: 293 + ping_setting: FULL_PING + platform_settings { + platform: PLATFORM_WINDOWS + danger_level: ALLOW_ON_USER_GESTURE + auto_open_hint: ALLOW_AUTO_OPEN + } + # TODO(nparker): Unpack these as ZIP files. https://crbug.com/628796 +} +file_types { + # A Visio file type. Added in https://crbug.com/771469 + extension: "vstx" + uma_value: 294 + ping_setting: FULL_PING + platform_settings { + platform: PLATFORM_WINDOWS + danger_level: ALLOW_ON_USER_GESTURE + auto_open_hint: ALLOW_AUTO_OPEN + } + # TODO(nparker): Unpack these as ZIP files. https://crbug.com/628796 +} +file_types { + # A Visio file type. Added in https://crbug.com/771469 + extension: "vsdm" + uma_value: 295 + ping_setting: FULL_PING + platform_settings { + platform: PLATFORM_WINDOWS + danger_level: ALLOW_ON_USER_GESTURE + auto_open_hint: ALLOW_AUTO_OPEN + } + # TODO(nparker): Unpack these as ZIP files. https://crbug.com/628796 +} +file_types { + # A Visio file type. Added in https://crbug.com/771469 + extension: "vssm" + uma_value: 296 + ping_setting: FULL_PING + platform_settings { + platform: PLATFORM_WINDOWS + danger_level: ALLOW_ON_USER_GESTURE + auto_open_hint: ALLOW_AUTO_OPEN + } + # TODO(nparker): Unpack these as ZIP files. https://crbug.com/628796 +} +file_types { + # A Visio file type. Added in https://crbug.com/771469 + extension: "vstm" + uma_value: 297 + ping_setting: FULL_PING + platform_settings { + platform: PLATFORM_WINDOWS + danger_level: ALLOW_ON_USER_GESTURE + auto_open_hint: ALLOW_AUTO_OPEN + } + # TODO(nparker): Unpack these as ZIP files. https://crbug.com/628796 +} +file_types { + # An older Visio file type. See https://crbug.com/771469 extension: "vsd" uma_value: 118 ping_setting: FULL_PING @@ -2175,6 +2357,7 @@ file_types { } } file_types { + # An older Visio file type. See https://crbug.com/771469 extension: "vss" uma_value: 120 ping_setting: FULL_PING @@ -2185,6 +2368,7 @@ file_types { } } file_types { + # An older Visio file type. See https://crbug.com/771469 extension: "vst" uma_value: 121 ping_setting: FULL_PING @@ -2221,30 +2405,18 @@ file_types { 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 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 bc42399d48f..42a110c6acf 100644 --- a/chromium/chrome/browser/resources/settings/about_page/about_page.html +++ b/chromium/chrome/browser/resources/settings/about_page/about_page.html @@ -104,7 +104,8 @@ src="[[getIconSrc_(obsoleteSystemInfo_, currentUpdateStatusEvent_)]]"> </iron-icon> <div class="start padded"> - <div id="updateStatusMessage" hidden="[[!showUpdateStatus_]]" + <div id="updateStatusMessage" hidden="[[!showUpdateStatus_]]"> + <div <if expr="not chromeos"> inner-h-t-m-l="[[getUpdateStatusMessage_( currentUpdateStatusEvent_)]]"> @@ -113,6 +114,12 @@ inner-h-t-m-l="[[getUpdateStatusMessage_( currentUpdateStatusEvent_, targetChannel_)]]"> </if> + </div> + <a hidden$="[[!shouldShowLearnMoreLink_( + currentUpdateStatusEvent_)]]" target="_blank" + href="https://support.google.com/chrome?p=update_error"> + $i18n{learnMore} + </a> </div> <span id="deprecationWarning" hidden="[[!obsoleteSystemInfo_.obsolete]]"> 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 5ddfb82ce1f..0ed1b715a19 100644 --- a/chromium/chrome/browser/resources/settings/about_page/about_page.js +++ b/chromium/chrome/browser/resources/settings/about_page/about_page.js @@ -283,6 +283,14 @@ Polymer({ }, /** + * @return {boolean} + * @private + */ + shouldShowLearnMoreLink_: function() { + return this.currentUpdateStatusEvent_.status == UpdateStatus.FAILED; + }, + + /** * @return {string} * @private */ diff --git a/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_page.html b/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_page.html index 0fed226e58a..a59514198c0 100644 --- a/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_page.html +++ b/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_page.html @@ -21,7 +21,8 @@ focus-config="[[focusConfig_]]"> <neon-animatable route-path="default"> <template is="dom-if" if="[[havePlayStoreApp]]" restamp> - <div id="android-apps" class="settings-box two-line first" actionable + <div id="android-apps" class="settings-box two-line first" + actionable$="[[androidAppsInfo.playStoreEnabled]]" on-tap="onSubpageTap_"> <div class="start"> $i18n{androidAppsPageLabel} diff --git a/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.js b/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.js index a0699eace97..9dee9d3149d 100644 --- a/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.js +++ b/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.js @@ -74,7 +74,7 @@ Polymer({ onConfirmDisableDialogConfirm_: function() { this.setPrefValue('arc.enabled', false); this.$.confirmDisableDialog.close(); - settings.navigateToPreviousRoute(); + // Sub-page will be closed in onAndroidAppsInfoUpdate_ call. }, /** 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 cb5157281cf..94029ec4769 100644 --- a/chromium/chrome/browser/resources/settings/basic_page/basic_page.html +++ b/chromium/chrome/browser/resources/settings/basic_page/basic_page.html @@ -27,7 +27,7 @@ <link rel="import" href="../default_browser_page/default_browser_page.html"> </if> -<if expr="is_win"> +<if expr="_google_chrome and is_win"> <link rel="import" href="../chrome_cleanup_page/chrome_cleanup_page.html"> </if> @@ -124,7 +124,7 @@ </settings-section> </template> </if> -<if expr="is_win"> +<if expr="_google_chrome and is_win"> <template is="dom-if" if="[[showChromeCleanup]]" restamp> <settings-section section="chromeCleanup"> <settings-chrome-cleanup-page></settings-chrome-cleanup-page> 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 36b2b7f6423..8207b5b3880 100644 --- a/chromium/chrome/browser/resources/settings/basic_page/basic_page.js +++ b/chromium/chrome/browser/resources/settings/basic_page/basic_page.js @@ -29,13 +29,15 @@ Polymer({ /** @type {!AndroidAppsInfo|undefined} */ androidAppsInfo: Object, + // <if expr="_google_chrome and is_win"> showChromeCleanup: { type: Boolean, value: function() { - return loadTimeData.valueExists('chromeCleanupEnabled') && - loadTimeData.getBoolean('chromeCleanupEnabled'); + return loadTimeData.getBoolean('chromeCleanupEnabled') && + !loadTimeData.getBoolean('userInitiatedCleanupsEnabled'); }, }, + // </if> showChangePassword: { type: Boolean, @@ -108,7 +110,7 @@ Polymer({ attached: function() { this.currentRoute_ = settings.getCurrentRoute(); - // <if expr="is_win"> + // <if expr="_google_chrome and is_win"> this.addEventListener('chrome-cleanup-dismissed', () => { this.showChromeCleanup = false; }); 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 dd816a8810d..2a6445caf7c 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_}, () => { - if (chrome.runtime.lastError) { - console.error( - 'Error enabling bluetooth: ' + - chrome.runtime.lastError.message); + var error = chrome.runtime.lastError; + if (error && error != 'Error setting adapter properties: powered') { + console.error('Error enabling bluetooth: ' + error.message); + return; } this.setPrefValue( 'ash.user.bluetooth.adapter_enabled', diff --git a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html index 7a782eeb0e5..e3fc960d500 100644 --- a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html +++ b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html @@ -39,7 +39,7 @@ } </style> - <div class="settings-box first"> + <div class="settings-box first" actionable on-tap="onEnableTap_"> <div id="onOff" class="start" on$="[[bluetoothToggleState]]"> [[getOnOffString_(bluetoothToggleState, '$i18nPolymer{deviceOn}', '$i18nPolymer{deviceOff}')]] @@ -47,7 +47,8 @@ <paper-toggle-button id="enableBluetooth" checked="{{bluetoothToggleState}}" disabled$="[[bluetoothToggleDisabled]]" - aria-label="$i18n{bluetoothToggleA11yLabel}"> + aria-label="$i18n{bluetoothToggleA11yLabel}" + on-tap="stopTap_"> </paper-toggle-button> </div> @@ -62,13 +63,12 @@ <div id="pairedContainer" class="container" scrollable on-device-event="onDeviceEvent_" hidden="[[!showDevices_(bluetoothToggleState, pairedDeviceList_)]]"> - <iron-list id="pairedDevices" class="vertical-list" preserve-focus - items="[[pairedDeviceList_]]" + <iron-list id="pairedDevices" preserve-focus items="[[pairedDeviceList_]]" selection-enabled selected-item="{{selectedPairedItem_}}" - scroll-target="pairedContainer"> + scroll-target="pairedContainer" class="cr-separators"> <template> <bluetooth-device-list-item actionable device="[[item]]" - tabindex$="[[tabIndex]]"> + first$="[[!index]]" tabindex$="[[tabIndex]]"> </bluetooth-device-list-item> </template> </iron-list> @@ -87,13 +87,13 @@ <div id="unpairedContainer" class="container" scrollable on-device-event="onDeviceEvent_" hidden="[[!showDevices_(bluetoothToggleState, unpairedDeviceList_)]]"> - <iron-list id="unpairedDevices" class="vertical-list" preserve-focus + <iron-list id="unpairedDevices" class="cr-separators" preserve-focus items="[[unpairedDeviceList_]]" selection-enabled selected-item="{{selectedUnpairedItem_}}" scroll-target="unpairedContainer"> <template> <bluetooth-device-list-item actionable device="[[item]]" - tabindex$="[[tabIndex]]"> + first$="[[!index]]" tabindex$="[[tabIndex]]"> </bluetooth-device-list-item> </template> </iron-list> @@ -102,7 +102,7 @@ <bluetooth-dialog id="deviceDialog" bluetooth="[[bluetooth]]" bluetooth-private="[[bluetoothPrivate]]" - title="$i18n{bluetoothPairDevicePageTitle}" + dialog-title="$i18n{bluetoothPairDevicePageTitle}" on-close="onDialogClose_" pairing-device="[[pairingDevice_]]"> </bluetooth-dialog> diff --git a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js index 54876622ff1..89131bed6c2 100644 --- a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js +++ b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js @@ -3,6 +3,12 @@ // found in the LICENSE file. /** + * Maximum number of bluetooth devices shown in bluetooth subpage. + * @const {number} + */ +var MAX_NUMBER_DEVICE_SHOWN = 50; + +/** * @fileoverview * 'settings-bluetooth-subpage' is the settings subpage for managing bluetooth * properties and devices. @@ -128,6 +134,15 @@ Polymer({ type: Object, value: chrome.bluetoothPrivate, }, + + /** + * Update frequency of the bluetooth list. + * @type {number} + */ + listUpdateFrequencyMs: { + type: Number, + value: 1000, + }, }, observers: [ @@ -136,13 +151,27 @@ Polymer({ ], /** - * Listener for chrome.bluetooth.onBluetoothDeviceAdded/Changed events. + * Timer ID for bluetooth list update. + * @type {number|undefined} + * @private + */ + updateTimerId_: undefined, + + /** + * Listener for chrome.bluetooth.onBluetoothDeviceChanged events. * @type {?function(!chrome.bluetooth.Device)} * @private */ bluetoothDeviceUpdatedListener_: null, /** + * Listener for chrome.bluetooth.onBluetoothDeviceAdded events. + * @type {?function(!chrome.bluetooth.Device)} + * @private + */ + bluetoothDeviceAddedListener_: null, + + /** * Listener for chrome.bluetooth.onBluetoothDeviceRemoved events. * @type {?function(!chrome.bluetooth.Device)} * @private @@ -154,11 +183,14 @@ Polymer({ this.bluetoothDeviceUpdatedListener_ = this.bluetoothDeviceUpdatedListener_ || this.onBluetoothDeviceUpdated_.bind(this); - this.bluetooth.onDeviceAdded.addListener( - this.bluetoothDeviceUpdatedListener_); this.bluetooth.onDeviceChanged.addListener( this.bluetoothDeviceUpdatedListener_); + this.bluetoothDeviceAddedListener_ = this.bluetoothDeviceAddedListener_ || + this.onBluetoothDeviceAdded_.bind(this); + this.bluetooth.onDeviceAdded.addListener( + this.bluetoothDeviceAddedListener_); + this.bluetoothDeviceRemovedListener_ = this.bluetoothDeviceRemovedListener_ || this.onBluetoothDeviceRemoved_.bind(this); @@ -169,7 +201,7 @@ Polymer({ /** @override */ detached: function() { this.bluetooth.onDeviceAdded.removeListener( - assert(this.bluetoothDeviceUpdatedListener_)); + assert(this.bluetoothDeviceAddedListener_)); this.bluetooth.onDeviceChanged.removeListener( assert(this.bluetoothDeviceUpdatedListener_)); this.bluetooth.onDeviceRemoved.removeListener( @@ -204,7 +236,7 @@ Polymer({ return !!device.paired || !!device.connecting; }); this.unpairedDeviceList_ = this.deviceList_.filter(function(device) { - return !device.paired; + return !device.paired && !device.connecting; }); this.updateScrollableContents(); this.restoreScroll(this.$.unpairedDevices); @@ -243,13 +275,11 @@ Polymer({ this.deviceList_ = []; return; } - this.bluetooth.getDevices(devices => { - this.deviceList_ = devices; - }); + this.requestListUpdate_(); }, /** - * Process bluetooth.onDeviceAdded and onDeviceChanged events. + * Process onDeviceChanged events. * @param {!chrome.bluetooth.Device} device * @private */ @@ -262,11 +292,17 @@ Polymer({ var index = this.deviceList_.findIndex(function(device) { return device.address == address; }); - if (index >= 0) { + if (index >= 0) this.set('deviceList_.' + index, device); - return; - } - this.push('deviceList_', device); + }, + + /** + * Process bluetooth.onDeviceAdded events. + * @param {!chrome.bluetooth.Device} device + * @private + */ + onBluetoothDeviceAdded_: function(device) { + this.requestListUpdate_(); }, /** @@ -331,6 +367,23 @@ Polymer({ }, /** + * @param {!Event} event + * @private + */ + stopTap_: function(event) { + event.stopPropagation(); + }, + + /** + * @param {!Event} event + * @private + */ + onEnableTap_: function(event) { + this.bluetoothToggleState = !this.bluetoothToggleState; + event.stopPropagation(); + }, + + /** * @param {boolean} enabled * @param {string} onstr * @param {string} offstr @@ -436,4 +489,77 @@ Polymer({ if (device) device.focus(); }, + + /** + * Requests update for bluetooth list. + * @private + */ + requestListUpdate_: function() { + if (this.deviceList_.length == 0) { + // Update immediately for the initial device list. + this.bluetooth.getDevices(devices => { + this.populateDeviceList_(devices); + }); + return; + } + + // Return here because an update is already queued. + if (this.updateTimerId_ !== undefined) + return; + + // Call bluetooth.getDevices once per listUpdateFrequencyMs. + this.updateTimerId_ = window.setTimeout(() => { + if (settings.getCurrentRoute() != settings.routes.BLUETOOTH_DEVICES) { + this.stopListUpdate_(); + return; + } + + this.bluetooth.getDevices(devices => { + this.populateDeviceList_(devices); + }); + this.updateTimerId_ = undefined; + }, this.listUpdateFrequencyMs); + }, + + /** + * Stops update for bluetooth list. + * @private + */ + stopListUpdate_: function() { + if (this.updateTimerId_ !== undefined) { + window.clearTimeout(this.updateTimerId_); + this.updateTimerId_ = undefined; + } + }, + + /** + * Populate the device list from chrome.bluetooth.getDevices + * Limit the device number to MAX_NUMBER_DEVICE_SHOWN and + * prioritize paired/connecting devices over other devices. + * @param {!Array<!chrome.bluetooth.Device|undefined>} devices + * @private + */ + populateDeviceList_: function(devices) { + var tempList = []; + var i; + for (i = 0; i < devices.length; i++) { + if (tempList.length == MAX_NUMBER_DEVICE_SHOWN) + break; + + if (!!devices[i].paired || !!devices[i].connecting) { + tempList.push(devices[i]); + devices[i] = undefined; + } + } + + for (i = 0; i < devices.length; i++) { + if (tempList.length == MAX_NUMBER_DEVICE_SHOWN) + break; + + if (devices[i] !== undefined) + tempList.push(devices[i]); + } + + this.deviceList_ = tempList; + }, }); 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 d2c2fc0ae4f..955204ac45c 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 @@ -98,11 +98,12 @@ <span>[[title_]]</span> <template is="dom-if" if="[[showDetails_]]"> <!-- Force line break to display learn-more inlined with - chromeCleanupExplanation or with the title_ (if showDetails_ is - false) despite these two elements being on different lines. --> + chromeCleanupExplanationRemove or with the title_ (if + showDetails_ is false) despite these two elements being on + different lines. --> <div></div> <span class="secondary"> - $i18n{chromeCleanupExplanation} + $i18n{chromeCleanupExplanationRemove} </span> </template> <a id="learn-more" href="$i18n{chromeCleanupLearnMoreUrl}" 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 502eac00064..d0b12514e16 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 @@ -104,7 +104,6 @@ } .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 * neighboring text that does not have an underline. */ diff --git a/chromium/chrome/browser/resources/settings/controls/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/controls/compiled_resources2.gyp index 518172c836e..32dadfbdef9 100644 --- a/chromium/chrome/browser/resources/settings/controls/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/controls/compiled_resources2.gyp @@ -101,6 +101,7 @@ { 'target_name': 'settings_toggle_button', 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/cr_toggle/compiled_resources2.gyp:cr_toggle', 'settings_boolean_control_behavior', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], 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 3e6a81d6d48..7cdb86fdff3 100644 --- a/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html +++ b/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html @@ -1,7 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> <link rel="import" href="settings_boolean_control_behavior.html"> <link rel="import" href="../settings_shared_css.html"> @@ -62,13 +62,12 @@ <cr-policy-pref-indicator pref="[[pref]]" icon-aria-label="[[label]]"> </cr-policy-pref-indicator> </template> - <paper-toggle-button id="control" checked="{{checked}}" - on-change="notifyChangedByUserInteraction" + <cr-toggle id="control" checked="{{checked}}" + on-change="onChange_" aria-label$="[[getAriaLabel_(label, ariaLabel)]]" - aria-describedby="subLabel" on-up="resetTrackLock_" - disabled="[[controlDisabled_(disabled, pref)]]" - on-tap="onToggleTap_"> - </paper-toggle-button> + aria-describedby="subLabel" + disabled="[[controlDisabled_(disabled, pref)]]"> + </cr-toggle> </div> </template> <script src="settings_toggle_button.js"></script> 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 96c789995a9..b4e7c83d4b3 100644 --- a/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.js +++ b/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.js @@ -58,17 +58,6 @@ Polymer({ return this.label || this.ariaLabel; }, - /** - * Handle taps directly on the toggle (see: onLabelWrapperTap_ for non-toggle - * taps). - * @param {!Event} e - * @private - */ - onToggleTap_: function(e) { - // Stop the event from propagating to avoid firing two 'changed' events. - e.stopPropagation(); - }, - /** @private */ onDisableOrPrefChange_: function() { if (this.controlDisabled_()) { @@ -79,28 +68,34 @@ Polymer({ }, /** - * Handle non-toggle button taps (see: onToggleTap_ for toggle taps). + * Handles non cr-toggle button taps (cr-toggle handles its own tap events + * which don't bubble). * @param {!Event} e * @private */ onHostTap_: function(e) { - // Stop the event from propagating to avoid firing two 'changed' events. e.stopPropagation(); if (this.controlDisabled_()) return; + // Ignore this |tap| event, if the interaction sequence + // (pointerdown+pointerup) began within the cr-toggle itself. + if (/** @type {!CrToggleElement} */ (this.$.control) + .shouldIgnoreHostTap(e)) { + return; + } + this.checked = !this.checked; this.notifyChangedByUserInteraction(); this.fire('change'); }, /** - * TODO(scottchen): temporary fix until polymer gesture bug resolved. See: - * https://github.com/PolymerElements/paper-slider/issues/186 + * @param {!CustomEvent} e * @private */ - resetTrackLock_: function() { - // Run tap.reset in next run-loop to avoid reversing the current tap event. - setTimeout(() => Polymer.Gestures.gestures.tap.reset()); + onChange_: function(e) { + this.checked = /** @type {boolean} */ (e.detail); + this.notifyChangedByUserInteraction(); }, }); diff --git a/chromium/chrome/browser/resources/settings/date_time_page/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/date_time_page/compiled_resources2.gyp index 0da9b331bfe..ad752f8f355 100644 --- a/chromium/chrome/browser/resources/settings/date_time_page/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/date_time_page/compiled_resources2.gyp @@ -6,13 +6,46 @@ { 'target_name': 'date_time_page', 'dependencies': [ - '../controls/compiled_resources2.gyp:settings_dropdown_menu', + '../compiled_resources2.gyp:route', '../prefs/compiled_resources2.gyp:prefs_behavior', '../prefs/compiled_resources2.gyp:prefs_types', '<(DEPTH)/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp:cr_policy_indicator_behavior', '<(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', + 'date_time_types', + 'timezone_selector', + 'timezone_subpage', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'date_time_types', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'timezone_selector', + 'dependencies': [ + '../controls/compiled_resources2.gyp:settings_dropdown_menu', + '../prefs/compiled_resources2.gyp:prefs_behavior', + '../prefs/compiled_resources2.gyp:prefs_types', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + 'date_time_types', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'timezone_subpage', + 'dependencies': [ + '../prefs/compiled_resources2.gyp:prefs_behavior', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + 'date_time_types', + 'timezone_selector', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, 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 0b573e387a1..d946c09c440 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 @@ -1,16 +1,21 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> +<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html"> <link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> -<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.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-toggle-button/paper-toggle-button.html"> -<link rel="import" href="../controls/settings_dropdown_menu.html"> <link rel="import" href="../controls/settings_toggle_button.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="../prefs/prefs_behavior.html"> <link rel="import" href="../prefs/prefs_types.html"> +<link rel="import" href="../route.html"> +<link rel="import" href="../settings_page/settings_subpage.html"> <link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="date_time_types.html"> +<link rel="import" href="timezone_selector.html"> +<link rel="import" href="timezone_subpage.html"> <dom-module id="settings-date-time-page"> <template> @@ -19,11 +24,14 @@ padding: 0; } - settings-dropdown-menu { - --md-select-width: 400px; + #timeZoneButton { + display: flex; + flex-direction: column; + justify-content: center; + width: 100%; } - paper-toggle-button { + cr-toggle { -webkit-margin-start: var(--settings-control-label-spacing); } @@ -31,76 +39,90 @@ -webkit-margin-start: var(--settings-controlled-by-spacing); } </style> - <div class="settings-box first"> - <div id="timezoneGeolocateToggleLabel" class="start"> - $i18n{timeZoneGeolocation} - </div> - <template is="dom-if" restamp - if="[[!prefs.cros.flags.per_user_timezone_enabled.value]]"> - <template is="dom-if" if="[[hasTimeZoneAutoDetectPolicy_]]" restamp> - <cr-policy-indicator indicator-type="devicePolicy" - icon-aria-label="$i18n{timeZoneGeolocation}"> - </cr-policy-indicator> - </template> - <paper-toggle-button - id="timeZoneAutoDetect" - aria-labelledby="timezoneGeolocateToggleLabel" - checked="[[timeZoneAutoDetect_]]" - disabled="[[hasTimeZoneAutoDetectPolicy_]]" - on-change="onTimeZoneAutoDetectChange_"> - </paper-toggle-button> - </template> - <template is="dom-if" restamp - if="[[prefs.cros.flags.per_user_timezone_enabled.value]]"> - <settings-toggle-button class="first" - pref="{{prefs.settings.resolve_timezone_by_geolocation}}" - id="timeZoneAutoDetect" - aria-label="$i18n{timeZoneGeolocation}"> - </settings-toggle-button> - </template> - </div> - <div class="settings-box continuation embedded"> - <template is="dom-if" restamp - if="[[!prefs.cros.flags.per_user_timezone_enabled.value]]"> - <settings-dropdown-menu pref="{{prefs.cros.system.timezone}}" - label="$i18n{timeZone}" - menu-options="[[timeZoneList_]]" - disabled="[[timeZoneAutoDetect_]]"> - </settings-dropdown-menu> - </template> - <template is="dom-if" restamp - if="[[prefs.cros.flags.per_user_timezone_enabled.value]]"> - <template is="dom-if" if="[[!isUserTimeZoneSelectorHidden_( - prefs.settings.timezone, - prefs.settings.resolve_timezone_by_geolocation.value)]]" restamp> - <settings-dropdown-menu id="userTimeZoneSelector" - pref="{{prefs.settings.timezone}}" - label="$i18n{timeZone}" - menu-options="[[timeZoneList_]]"> - </settings-dropdown-menu> + <settings-animated-pages id="pages" section="dateTime" + focus-config="[[focusConfig_]]"> + <neon-animatable id="main" route-path="default"> + <template is="dom-if" + if="[[!prefs.cros.flags.fine_grained_time_zone_detection_enabled.value]]" + restamp> + <div class="settings-box first"> + <div id="timezoneGeolocateToggleLabel" class="start"> + $i18n{timeZoneGeolocation} + </div> + <template is="dom-if" + if="[[hasTimeZoneAutoDetectPolicyRestriction_]]" restamp> + <cr-policy-indicator indicator-type="devicePolicy" + icon-aria-label="$i18n{timeZoneGeolocation}"> + </cr-policy-indicator> + </template> + <cr-toggle + id="timeZoneAutoDetect" + aria-label="$i18n{timeZoneGeolocation}" + checked="[[timeZoneAutoDetect_]]" + disabled="[[hasTimeZoneAutoDetectPolicyRestriction_]]" + on-change="onTimeZoneAutoDetectChange_"> + </cr-toggle> + </div> </template> - <template is="dom-if" if="[[isUserTimeZoneSelectorHidden_( - prefs.settings.timezone, - prefs.settings.resolve_timezone_by_geolocation.value)]]" restamp> - <settings-dropdown-menu id="systemTimezoneSelector" - pref="{{prefs.cros.system.timezone}}" - label="$i18n{timeZone}" - menu-options="[[timeZoneList_]]" - disabled> - </settings-dropdown-menu> + <template is="dom-if" + if="[[prefs.cros.flags.fine_grained_time_zone_detection_enabled.value]]" + restamp> + <div id="timeZoneSettingsTrigger" class="settings-box first" + on-tap="onTimeZoneSettings_" actionable> + <div id="timeZoneButton" class="two-line"> + $i18n{timeZoneButton} + <div class="secondary"> + <div hidden="[[timeZoneAutoDetect_]]"> + [[activeTimeZoneDisplayName]] + </div> + <div hidden="[[!timeZoneAutoDetect_]]"> + [[getTimeZoneAutoDetectMethodDisplayName_( + timeZoneAutoDetectMethod_)]] + </div> + </div> + </div> + <template is="dom-if" + if="[[hasTimeZoneAutoDetectPolicyRestriction_]]" restamp> + <cr-policy-indicator indicator-type="devicePolicy" + icon-aria-label="$i18n{timeZoneGeolocation}" + hidden="[[!hasTimeZoneAutoDetectPolicyRestriction_]]"> + </cr-policy-indicator> + </template> + <button class="subpage-arrow" + disabled="[[hasTimeZoneAutoDetectPolicyRestriction_]]" + is="paper-icon-button-light" + aria-label="$i18n{timeZoneButton}"></button> + </div> </template> + <div class="settings-box continuation embedded" + hidden="[[prefs.cros.flags.fine_grained_time_zone_detection_enabled.value]]"> + <timezone-selector prefs="{{prefs}}" + time-zone-auto-detect="[[timeZoneAutoDetect_]]" + active-time-zone-display-name="{{activeTimeZoneDisplayName}}"> + </timezone-selector> + </div> + <settings-toggle-button + pref="{{prefs.settings.clock.use_24hour_clock}}" + label="$i18n{use24HourClock}"> + </settings-toggle-button> + <div class="settings-box" id="setDateTime" actionable + on-tap="onSetDateTimeTap_" hidden$="[[!canSetDateTime_]]"> + <div class="start">$i18n{setDateTime}</div> + <button class="subpage-arrow" is="paper-icon-button-light" + aria-label="$i18n{setDateTime}"></button> + </div> + </neon-animatable> + <template is="dom-if" route-path="/dateTime/timeZone"> + <settings-subpage data-route="DATETIME_TIMEZONE_SUBPAGE" + associated-control="[[$$('#timeZoneSettingsTrigger')]]" + page-title="$i18n{timeZoneSubpageTitle}"> + <timezone-subpage id="timezoneSubpage" prefs="{{prefs}}" + time-zone-auto-detect="[[timeZoneAutoDetect_]]" + active-time-zone-display-name="{{activeTimeZoneDisplayName}}"> + </timezone-subpage> + </settings-subpage> </template> - </div> - <settings-toggle-button - pref="{{prefs.settings.clock.use_24hour_clock}}" - label="$i18n{use24HourClock}"> - </settings-toggle-button> - <div class="settings-box" id="setDateTime" actionable - on-tap="onSetDateTimeTap_" hidden$="[[!canSetDateTime_]]"> - <div class="start">$i18n{setDateTime}</div> - <button class="subpage-arrow" is="paper-icon-button-light" - aria-label="$i18n{setDateTime}"></button> - </div> + </settings-animated-pages> </template> <script src="date_time_page.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.js b/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.js index ef378947636..09badd25ad5 100644 --- a/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.js +++ b/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.js @@ -8,36 +8,24 @@ * settings. */ -cr.exportPath('settings'); - -/** - * Describes the status of the auto-detect policy. - * @enum {number} - */ -settings.TimeZoneAutoDetectPolicy = { - NONE: 0, - FORCED_ON: 1, - FORCED_OFF: 2, -}; - Polymer({ is: 'settings-date-time-page', - behaviors: [PrefsBehavior, WebUIListenerBehavior], + behaviors: [I18nBehavior, PrefsBehavior, WebUIListenerBehavior], properties: { /** - * The time zone auto-detect policy. - * @private {settings.TimeZoneAutoDetectPolicy} + * The effective policy restriction on time zone automatic detection. + * @private {settings.TimeZoneAutoDetectPolicyRestriction} */ - timeZoneAutoDetectPolicy_: { - type: Boolean, + timeZoneAutoDetectPolicyRestriction_: { + type: Number, value: function() { if (!loadTimeData.valueExists('timeZoneAutoDetectValueFromPolicy')) - return settings.TimeZoneAutoDetectPolicy.NONE; + return settings.TimeZoneAutoDetectPolicyRestriction.NONE; return loadTimeData.getBoolean('timeZoneAutoDetectValueFromPolicy') ? - settings.TimeZoneAutoDetectPolicy.FORCED_ON : - settings.TimeZoneAutoDetectPolicy.FORCED_OFF; + settings.TimeZoneAutoDetectPolicyRestriction.FORCED_ON : + settings.TimeZoneAutoDetectPolicyRestriction.FORCED_OFF; }, }, @@ -45,37 +33,32 @@ Polymer({ * Whether a policy controls the time zone auto-detect setting. * @private */ - hasTimeZoneAutoDetectPolicy_: { + hasTimeZoneAutoDetectPolicyRestriction_: { type: Boolean, - computed: - 'computeHasTimeZoneAutoDetectPolicy_(timeZoneAutoDetectPolicy_)', + computed: 'computeHasTimeZoneAutoDetectPolicy_(' + + 'timeZoneAutoDetectPolicyRestriction_)', }, /** - * The effective time zone auto-detect setting. + * The effective time zone auto-detect enabled/disabled status. * @private */ timeZoneAutoDetect_: { type: Boolean, computed: 'computeTimeZoneAutoDetect_(' + - 'timeZoneAutoDetectPolicy_,' + - 'prefs.settings.resolve_timezone_by_geolocation.value)', + 'timeZoneAutoDetectPolicyRestriction_,' + + 'prefs.settings.resolve_timezone_by_geolocation_method.value)', }, /** - * Initialized with the current time zone so the menu displays the - * correct value. The full option list is fetched lazily if necessary by - * maybeGetTimeZoneList_. - * @private {!DropdownMenuOptionList} + * The effective time zone auto-detect method. + * @private {settings.TimeZoneAutoDetectMethod} */ - timeZoneList_: { - type: Array, - value: function() { - return [{ - name: loadTimeData.getString('timeZoneName'), - value: loadTimeData.getString('timeZoneID'), - }]; - }, + timeZoneAutoDetectMethod_: { + type: Number, + computed: 'computeTimeZoneAutoDetectMethod_(' + + 'hasTimeZoneAutoDetectPolicyRestriction_,' + + 'prefs.settings.resolve_timezone_by_geolocation_method.value)', }, /** @@ -87,14 +70,29 @@ Polymer({ type: Boolean, value: false, }, - }, - observers: [ - 'maybeGetTimeZoneListPerUser_(' + - 'prefs.settings.timezone.value, timeZoneAutoDetect_)', - 'maybeGetTimeZoneListPerSystem_(' + - 'prefs.cros.system.timezone.value, timeZoneAutoDetect_)', - ], + /** + * This is used to get current time zone display name from + * <timezone-selector> via bi-directional binding. + */ + activeTimeZoneDisplayName: { + type: String, + value: loadTimeData.getString('timeZoneName'), + }, + + /** @private {!Map<string, string>} */ + focusConfig_: { + type: Object, + value: function() { + var map = new Map(); + if (settings.routes.DATETIME_TIMEZONE_SUBPAGE) + map.set( + settings.routes.DATETIME_TIMEZONE_SUBPAGE.path, + '#timeZoneSettingsTrigger .subpage-arrow'); + return map; + }, + }, + }, /** @override */ attached: function() { @@ -105,7 +103,6 @@ Polymer({ 'can-set-date-time-changed', this.onCanSetDateTimeChanged_.bind(this)); chrome.send('dateTimePageReady'); - this.maybeGetTimeZoneList_(); }, /** @@ -116,11 +113,12 @@ Polymer({ */ onTimeZoneAutoDetectPolicyChanged_: function(managed, valueFromPolicy) { if (managed) { - this.timeZoneAutoDetectPolicy_ = valueFromPolicy ? - settings.TimeZoneAutoDetectPolicy.FORCED_ON : - settings.TimeZoneAutoDetectPolicy.FORCED_OFF; + this.timeZoneAutoDetectPolicyRestriction_ = valueFromPolicy ? + settings.TimeZoneAutoDetectPolicyRestriction.FORCED_ON : + settings.TimeZoneAutoDetectPolicyRestriction.FORCED_OFF; } else { - this.timeZoneAutoDetectPolicy_ = settings.TimeZoneAutoDetectPolicy.NONE; + this.timeZoneAutoDetectPolicyRestriction_ = + settings.TimeZoneAutoDetectPolicyRestriction.NONE; } }, @@ -138,7 +136,9 @@ Polymer({ */ onTimeZoneAutoDetectChange_: function(e) { this.setPrefValue( - 'settings.resolve_timezone_by_geolocation', e.target.checked); + 'settings.resolve_timezone_by_geolocation_method', + e.target.checked ? settings.TimeZoneAutoDetectMethod.IP_ONLY : + settings.TimeZoneAutoDetectMethod.DISABLED); }, /** @private */ @@ -147,106 +147,100 @@ Polymer({ }, /** - * @param {settings.TimeZoneAutoDetectPolicy} timeZoneAutoDetectPolicy + * @param {settings.TimeZoneAutoDetectPolicyRestriction} policyValue * @return {boolean} * @private */ - computeHasTimeZoneAutoDetectPolicy_: function(timeZoneAutoDetectPolicy) { - return timeZoneAutoDetectPolicy != settings.TimeZoneAutoDetectPolicy.NONE; + computeHasTimeZoneAutoDetectPolicy_: function(policyValue) { + return policyValue != settings.TimeZoneAutoDetectPolicyRestriction.NONE; }, /** - * @param {settings.TimeZoneAutoDetectPolicy} timeZoneAutoDetectPolicy - * @param {boolean} prefValue Value of the geolocation pref. + * @param {settings.TimeZoneAutoDetectPolicyRestriction} policyValue + * @param {settings.TimeZoneAutoDetectMethod} prefValue + * prefs.settings.resolve_timezone_by_geolocation_method.value * @return {boolean} Whether time zone auto-detect is enabled. * @private */ - computeTimeZoneAutoDetect_: function(timeZoneAutoDetectPolicy, prefValue) { - switch (timeZoneAutoDetectPolicy) { - case settings.TimeZoneAutoDetectPolicy.NONE: - return prefValue; - case settings.TimeZoneAutoDetectPolicy.FORCED_ON: + computeTimeZoneAutoDetect_: function(policyValue, prefValue) { + switch (policyValue) { + case settings.TimeZoneAutoDetectPolicyRestriction.NONE: + return prefValue != settings.TimeZoneAutoDetectMethod.DISABLED; + case settings.TimeZoneAutoDetectPolicyRestriction.FORCED_ON: return true; - case settings.TimeZoneAutoDetectPolicy.FORCED_OFF: + case settings.TimeZoneAutoDetectPolicyRestriction.FORCED_OFF: return false; default: - assertNotReached(); + console.error('Unknown policy value "' + policyValue + '".'); + return false; } }, /** - * Fetches the list of time zones if necessary. - * @param {boolean=} perUserTimeZoneMode Expected value of per-user time zone. + * Computes effective time zone detection method. + * @param {Boolean} hasTimeZoneAutoDetectPolicyRestriction + * this.hasTimeZoneAutoDetectPolicyRestriction_ + * @param {settings.TimeZoneAutoDetectMethod} prefResolveValue + * prefs.settings.resolve_timezone_by_geolocation_method.value + * @return {settings.TimeZoneAutoDetectMethod} * @private */ - maybeGetTimeZoneList_: function(perUserTimeZoneMode) { - if (typeof(perUserTimeZoneMode) !== 'undefined') { - /* This method is called as observer. Skip if if current mode does not - * match expected. - */ - if (perUserTimeZoneMode != - this.getPref('cros.flags.per_user_timezone_enabled').value) { - return; + computeTimeZoneAutoDetectMethod_: function( + hasTimeZoneAutoDetectPolicyRestriction, prefResolveValue) { + if (hasTimeZoneAutoDetectPolicyRestriction) { + // timeZoneAutoDetectPolicyRestriction_ actually depends on several time + // policies and chrome flags. So we ignore real policy value if it is + // disabled. + if (this.timeZoneAutoDetectPolicyRestriction_ == + settings.TimeZoneAutoDetectPolicyRestriction.FORCED_OFF) { + return settings.TimeZoneAutoDetectMethod.DISABLED; } - } - // Only fetch the list once. - if (this.timeZoneList_.length > 1 || !CrSettingsPrefs.isInitialized) - return; - - // If auto-detect is enabled, we only need the current time zone. - if (this.timeZoneAutoDetect_) { - var isPerUserTimezone = - this.getPref('cros.flags.per_user_timezone_enabled').value; - if (this.timeZoneList_[0].value == - (isPerUserTimezone ? this.getPref('settings.timezone').value : - this.getPref('cros.system.timezone').value)) { - return; + + var policyValue = /** @type{settings.SystemTimezoneProto} */ ( + this.getPref('settings.resolve_device_timezone_by_geolocation_policy') + .value); + + switch (policyValue) { + case settings.SystemTimezoneProto.USERS_DECIDE: + console.error('Unexpected policy value "' + policyValue + '".'); + return settings.TimeZoneAutoDetectMethod.DISABLED; + case settings.SystemTimezoneProto.DISABLED: + return settings.TimeZoneAutoDetectMethod.DISABLED; + case settings.SystemTimezoneProto.IP_ONLY: + return settings.TimeZoneAutoDetectMethod.IP_ONLY; + case settings.SystemTimezoneProto.SEND_WIFI_ACCESS_POINTS: + return settings.TimeZoneAutoDetectMethod.SEND_WIFI_ACCESS_POINTS; + case settings.SystemTimezoneProto.SEND_ALL_LOCATION_INFO: + return settings.TimeZoneAutoDetectMethod.SEND_ALL_LOCATION_INFO; + default: + return settings.TimeZoneAutoDetectMethod.DISABLED; } } - - cr.sendWithPromise('getTimeZones').then(this.setTimeZoneList_.bind(this)); - }, - - /** - * Prefs observer for Per-user time zone enabled mode. - * @private - */ - maybeGetTimeZoneListPerUser_: function() { - this.maybeGetTimeZoneList_(true); + return prefResolveValue; }, /** - * Prefs observer for Per-user time zone disabled mode. + * Returns display name of the given time zone detection method. + * @param {settings.TimeZoneAutoDetectMethod} method + * this.timeZoneAutoDetectMethod_ value. + * @return {string} * @private */ - maybeGetTimeZoneListPerSystem_: function() { - this.maybeGetTimeZoneList_(false); + getTimeZoneAutoDetectMethodDisplayName_: function(method) { + var id = ([ + 'setTimeZoneAutomaticallyDisabled', + 'setTimeZoneAutomaticallyIpOnlyDefault', + 'setTimeZoneAutomaticallyWithWiFiAccessPointsData', + 'setTimeZoneAutomaticallyWithAllLocationInfo' + ])[method]; + if (id) + return this.i18n(id); + + return ''; }, - /** - * Converts the C++ response into an array of menu options. - * @param {!Array<!Array<string>>} timeZones C++ time zones response. - * @private - */ - setTimeZoneList_: function(timeZones) { - this.timeZoneList_ = timeZones.map(function(timeZonePair) { - return { - name: timeZonePair[1], - value: timeZonePair[0], - }; - }); - }, - - /** - * Computes visibility of user timezone preference. - * @param {?chrome.settingsPrivate.PrefObject} prefUserTimezone - * pref.settings.timezone - * @param {boolean} prefResolveValue - * prefs.settings.resolve_timezone_by_geolocation.value - * @private - */ - isUserTimeZoneSelectorHidden_: function(prefUserTimezone, prefResolveValue) { - return (prefUserTimezone && prefUserTimezone.controlledBy != null) || - prefResolveValue; + onTimeZoneSettings_: function() { + // TODO(alemate): revise this once UI mocks are finished. + settings.navigateTo(settings.routes.DATETIME_TIMEZONE_SUBPAGE); }, }); diff --git a/chromium/chrome/browser/resources/settings/date_time_page/date_time_types.html b/chromium/chrome/browser/resources/settings/date_time_page/date_time_types.html new file mode 100644 index 00000000000..b3ecb802c52 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/date_time_page/date_time_types.html @@ -0,0 +1,2 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="date_time_types.js"></script> diff --git a/chromium/chrome/browser/resources/settings/date_time_page/date_time_types.js b/chromium/chrome/browser/resources/settings/date_time_page/date_time_types.js new file mode 100644 index 00000000000..241d9b21e0c --- /dev/null +++ b/chromium/chrome/browser/resources/settings/date_time_page/date_time_types.js @@ -0,0 +1,48 @@ +// 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 + * This defines some types for settings-date-time-page. + */ + +cr.exportPath('settings'); + +/** + * Describes the effective policy restriction on time zone automatic detection. + * @enum {number} + */ +settings.TimeZoneAutoDetectPolicyRestriction = { + NONE: 0, + FORCED_ON: 1, + FORCED_OFF: 2, +}; + +/** + * Describes values of prefs.settings.resolve_timezone_by_geolocation_method. + * Must be kept in sync with TimeZoneResolverManager::TimeZoneResolveMethod + * enum. + * @enum {number} + */ +settings.TimeZoneAutoDetectMethod = { + DISABLED: 0, + IP_ONLY: 1, + SEND_WIFI_ACCESS_POINTS: 2, + SEND_ALL_LOCATION_INFO: 3 +}; + +/** + * Describes values of prefs.settings. + * resolve_device_timezone_by_geolocation_policy + * Must be kept in sync with enterprise_management::SystemTimezoneProto + * enum. + * @enum {number} + */ +settings.SystemTimezoneProto = { + USERS_DECIDE: 0, + DISABLED: 1, + IP_ONLY: 2, + SEND_WIFI_ACCESS_POINTS: 3, + SEND_ALL_LOCATION_INFO: 4, +}; diff --git a/chromium/chrome/browser/resources/settings/date_time_page/timezone_selector.html b/chromium/chrome/browser/resources/settings/date_time_page/timezone_selector.html new file mode 100644 index 00000000000..10e03126f46 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/date_time_page/timezone_selector.html @@ -0,0 +1,52 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="../controls/settings_dropdown_menu.html"> +<link rel="import" href="../i18n_setup.html"> +<link rel="import" href="../prefs/prefs_behavior.html"> +<link rel="import" href="../prefs/prefs_types.html"> +<link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="date_time_types.html"> + +<dom-module id="timezone-selector"> + <template> + <style include="settings-shared"> + settings-dropdown-menu { + --md-select-width: 400px; + } + </style> + <template is="dom-if" restamp + if="[[!prefs.cros.flags.per_user_timezone_enabled.value]]"> + <settings-dropdown-menu pref="{{prefs.cros.system.timezone}}" + label="$i18n{timeZoneColon}" + menu-options="[[timeZoneList_]]" + disabled="[[timeZoneAutoDetect]]"> + </settings-dropdown-menu> + </template> + <template is="dom-if" restamp + if="[[prefs.cros.flags.per_user_timezone_enabled.value]]"> + <template is="dom-if" if="[[!isUserTimeZoneSelectorHidden_( + prefs.settings.timezone, + prefs.settings.resolve_timezone_by_geolocation_method.value)]]" + restamp> + <settings-dropdown-menu id="userTimeZoneSelector" + pref="{{prefs.settings.timezone}}" + label="$i18n{timeZoneColon}" + menu-options="[[timeZoneList_]]"> + </settings-dropdown-menu> + </template> + <template is="dom-if" if="[[isUserTimeZoneSelectorHidden_( + prefs.settings.timezone, + prefs.settings.resolve_timezone_by_geolocation_method.value)]]" + restamp> + <settings-dropdown-menu id="systemTimezoneSelector" + pref="{{prefs.cros.system.timezone}}" + label="$i18n{timeZoneColon}" + menu-options="[[timeZoneList_]]" + disabled> + </settings-dropdown-menu> + </template> + </template> + </template> + <script src="timezone_selector.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/settings/date_time_page/timezone_selector.js b/chromium/chrome/browser/resources/settings/date_time_page/timezone_selector.js new file mode 100644 index 00000000000..e6559db6fbd --- /dev/null +++ b/chromium/chrome/browser/resources/settings/date_time_page/timezone_selector.js @@ -0,0 +1,153 @@ +// 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 'timezone-selector' is the time zone selector dropdown. + */ +(function() { +'use strict'; + +Polymer({ + is: 'timezone-selector', + + behaviors: [I18nBehavior, PrefsBehavior], + + properties: { + /** + * If time zone auto detectoin is enabled. + */ + timeZoneAutoDetect: Boolean, + + /** + * This stores active time zone display name to be used in other UI + * via bi-directional binding. + */ + activeTimeZoneDisplayName: { + type: String, + notify: true, + }, + + /** + * Initialized with the current time zone so the menu displays the + * correct value. The full option list is fetched lazily if necessary by + * maybeGetTimeZoneList_. + * @private {!DropdownMenuOptionList} + */ + timeZoneList_: { + type: Array, + value: function() { + return [{ + name: loadTimeData.getString('timeZoneName'), + value: loadTimeData.getString('timeZoneID'), + }]; + }, + }, + }, + + observers: [ + 'maybeGetTimeZoneListPerUser_(' + + 'prefs.settings.timezone.value, timeZoneAutoDetect)', + 'maybeGetTimeZoneListPerSystem_(' + + 'prefs.cros.system.timezone.value, timeZoneAutoDetect)', + 'updateActiveTimeZoneName_(prefs.cros.system.timezone.value)', + ], + + /** @override */ + attached: function() { + this.maybeGetTimeZoneList_(); + }, + + /** + * Fetches the list of time zones if necessary. + * @param {boolean=} perUserTimeZoneMode Expected value of per-user time zone. + * @private + */ + maybeGetTimeZoneList_: function(perUserTimeZoneMode) { + if (typeof(perUserTimeZoneMode) !== 'undefined') { + /* This method is called as observer. Skip if if current mode does not + * match expected. + */ + if (perUserTimeZoneMode != + this.getPref('cros.flags.per_user_timezone_enabled').value) { + return; + } + } + // Only fetch the list once. + if (this.timeZoneList_.length > 1 || !CrSettingsPrefs.isInitialized) + return; + + // If auto-detect is enabled, we only need the current time zone. + if (this.timeZoneAutoDetect) { + var isPerUserTimezone = + this.getPref('cros.flags.per_user_timezone_enabled').value; + if (this.timeZoneList_[0].value == + (isPerUserTimezone ? this.getPref('settings.timezone').value : + this.getPref('cros.system.timezone').value)) { + return; + } + } + + cr.sendWithPromise('getTimeZones').then(this.setTimeZoneList_.bind(this)); + }, + + /** + * Prefs observer for Per-user time zone enabled mode. + * @private + */ + maybeGetTimeZoneListPerUser_: function() { + this.maybeGetTimeZoneList_(true); + }, + + /** + * Prefs observer for Per-user time zone disabled mode. + * @private + */ + maybeGetTimeZoneListPerSystem_: function() { + this.maybeGetTimeZoneList_(false); + }, + + /** + * Converts the C++ response into an array of menu options. + * @param {!Array<!Array<string>>} timeZones C++ time zones response. + * @private + */ + setTimeZoneList_: function(timeZones) { + this.timeZoneList_ = timeZones.map(function(timeZonePair) { + return { + name: timeZonePair[1], + value: timeZonePair[0], + }; + }); + this.updateActiveTimeZoneName_( + /** @type {!String} */ (this.getPref('cros.system.timezone').value)); + }, + + /** + * Updates active time zone display name when changed. + * @param {!String} activeTimeZoneId value of cros.system.timezone preference. + * @private + */ + updateActiveTimeZoneName_: function(activeTimeZoneId) { + var activeTimeZone = this.timeZoneList_.find( + (timeZone) => timeZone.value == activeTimeZoneId); + if (activeTimeZone) + this.activeTimeZoneDisplayName = activeTimeZone.name; + }, + + + /** + * Computes visibility of user timezone preference. + * @param {?chrome.settingsPrivate.PrefObject} prefUserTimezone + * pref.settings.timezone + * @param {settings.TimeZoneAutoDetectMethod} prefResolveValue + * prefs.settings.resolve_timezone_by_geolocation_method.value + * @return {boolean} + * @private + */ + isUserTimeZoneSelectorHidden_: function(prefUserTimezone, prefResolveValue) { + return (prefUserTimezone && prefUserTimezone.controlledBy != null) || + prefResolveValue != settings.TimeZoneAutoDetectMethod.DISABLED; + }, +}); +})(); diff --git a/chromium/chrome/browser/resources/settings/date_time_page/timezone_subpage.html b/chromium/chrome/browser/resources/settings/date_time_page/timezone_subpage.html new file mode 100644 index 00000000000..200f175ffed --- /dev/null +++ b/chromium/chrome/browser/resources/settings/date_time_page/timezone_subpage.html @@ -0,0 +1,54 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="../controls/controlled_radio_button.html"> +<link rel="import" href="../controls/settings_radio_group.html"> +<link rel="import" href="../prefs/prefs.html"> +<link rel="import" href="../prefs/prefs_behavior.html"> +<link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="date_time_types.html"> + +<dom-module id="timezone-subpage"> + <template> + <style include="settings-shared"> + .block { + display: block; + } + </style> + <div class="settings-box block first"> + <settings-radio-group id="timeZoneRadioGroup" + pref="{{prefs.settings.resolve_timezone_by_geolocation_method}}"> + <controlled-radio-button + name="[[timezoneAutodetectMethodValues_.IP_ONLY]]" + pref="[[prefs.settings.resolve_timezone_by_geolocation_method]]" + label="$i18n{setTimeZoneAutomaticallyIpOnlyDefault}" + no-extension-indicator> + <div class="secondary"> + $i18n{setTimeZoneAutomaticallyIpOnlyDefaultDescription} + </div> + </controlled-radio-button> + <controlled-radio-button + name="[[timezoneAutodetectMethodValues_.SEND_ALL_LOCATION_INFO]]" + pref="[[prefs.settings.resolve_timezone_by_geolocation_method]]" + label="$i18n{setTimeZoneAutomaticallyWithAllLocationInfo}" + no-extension-indicator> + <div class="secondary"> + $i18n{setTimeZoneAutomaticallyWithAllLocationInfoDescription} + </div> + </controlled-radio-button> + <controlled-radio-button + name="[[timezoneAutodetectMethodValues_.DISABLED]]" + pref="[[prefs.settings.resolve_timezone_by_geolocation_method]]" + label="$i18n{selectYourTimeZone}" + no-extension-indicator> + </controlled-radio-button> + </settings-radio-group> + </div> + <div class="settings-box block"> + <timezone-selector prefs="{{prefs}}" + time-zone-auto-detect="[[timeZoneAutoDetect]]" + active-time-zone-display-name="{{activeTimeZoneDisplayName}}"> + </timezone-selector> + </div> + </template> + <script src="timezone_subpage.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/settings/date_time_page/timezone_subpage.js b/chromium/chrome/browser/resources/settings/date_time_page/timezone_subpage.js new file mode 100644 index 00000000000..96109be8c76 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/date_time_page/timezone_subpage.js @@ -0,0 +1,43 @@ +// 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 'timezone-subpage' is the collapsible section containing + * time zone settings. + */ +(function() { +'use strict'; + +Polymer({ + is: 'timezone-subpage', + + behaviors: [PrefsBehavior], + + properties: { + /** + * This is <timezone-selector> parameter. + */ + activeTimeZoneDisplayName: { + type: String, + notify: true, + }, + + /** + * The effective time zone auto-detect enabled/disabled status. + */ + timeZoneAutoDetect: Boolean, + + /** + * settings.TimeZoneAutoDetectMethod values. + * @private {!Object<settings.TimeZoneAutoDetectMethod, number>} + */ + timezoneAutodetectMethodValues_: Object, + + }, + + attached: function() { + this.timezoneAutodetectMethodValues_ = settings.TimeZoneAutoDetectMethod; + }, +}); +})(); diff --git a/chromium/chrome/browser/resources/settings/device_page/display.html b/chromium/chrome/browser/resources/settings/device_page/display.html index 0602de00131..b3e57b250ca 100644 --- a/chromium/chrome/browser/resources/settings/device_page/display.html +++ b/chromium/chrome/browser/resources/settings/device_page/display.html @@ -1,6 +1,7 @@ <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/cr_elements/cr_toggle/cr_toggle.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"> @@ -9,7 +10,6 @@ <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-tabs/paper-tabs.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> <link rel="import" href="display_layout.html"> <link rel="import" href="display_overscan_dialog.html"> <link rel="import" href="night_light_slider.html"> @@ -28,6 +28,11 @@ padding: 0; } + #nightLightTemperatureDiv[disabled] { + opacity: 0.38; + pointer-events: none; + } + .display-tabs { width: 100%; } @@ -72,6 +77,18 @@ selected-display="[[selectedDisplay]]" on-select-display="onSelectDisplay_"> </display-layout> + + <template is="dom-if" if="[[showMirror_(unifiedDesktopMode_, displays)]]" + restamp> + <div class="secondary self-start"> + <paper-checkbox checked="[[isMirrored_(displays)]]" + on-tap="onMirroredTap_" + aria-label="[[getDisplayMirrorText_(displays)]]"> + <div class="text-area">[[getDisplayMirrorText_(displays)]]</div> + </paper-checkbox> + </div> + </template> + </div> <div hidden="[[!hasMultipleDisplays_(displays)]]" class="settings-box"> <paper-tabs noink selected="[[selectedDisplay.id]]" class="display-tabs" @@ -86,21 +103,6 @@ <div id="controlsDiv" class="settings-box layout vertical first"> <h2>[[selectedDisplay.name]]</h2> - - <template is="dom-if" if="[[showMirror_(unifiedDesktopMode_, displays)]]" - restamp> - <div class="settings-box indented two-line"> - <div class="start"> - <div id="displayMirrorCheckboxLabel">$i18n{displayMirror}</div> - <div class="secondary">[[getDisplayMirrorText_(displays)]]</div> - </div> - <paper-toggle-button checked="[[isMirrored_(displays)]]" - on-tap="onMirroredTap_" - aria-labelledby="displayMirrorCheckboxLabel"> - </paper-toggle-button> - </div> - </template> - <template is="dom-if" if="[[showUnifiedDesktop_(unifiedDesktopAvailable_, unifiedDesktopMode_, displays)]]" restamp> <div class="settings-box indented two-line"> @@ -112,10 +114,10 @@ [[getUnifiedDesktopText_(unifiedDesktopMode_)]] </div> </div> - <paper-toggle-button checked="[[unifiedDesktopMode_]]" + <cr-toggle checked="[[unifiedDesktopMode_]]" on-tap="onUnifiedDesktopTap_" aria-labelledby="displayUnifiedDesktopCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> </template> @@ -208,7 +210,9 @@ <div id="nightLightSettingsDiv" class="settings-box continuation start layout vertical"> <!-- Color temperature slider --> - <div class="settings-box indented continuation"> + <div id="nightLightTemperatureDiv" + class="settings-box indented continuation" + disabled$="[[!prefs.ash.night_light.enabled.value]]"> <div class="start text-area" id="colorTemperatureLabel"> $i18n{displayNightLightTemperatureLabel} </div> @@ -216,7 +220,8 @@ aria-labelledby="colorTemperatureLabel" min="0" max="100" scale="100" label-min="$i18n{displayNightLightTempSliderMinLabel}" label-max="$i18n{displayNightLightTempSliderMaxLabel}" - pref="{{prefs.ash.night_light.color_temperature}}"> + pref="{{prefs.ash.night_light.color_temperature}}" + disabled$="[[!prefs.ash.night_light.enabled.value]]"> </settings-slider> </div> <!-- Schedule settings --> diff --git a/chromium/chrome/browser/resources/settings/device_page/display.js b/chromium/chrome/browser/resources/settings/device_page/display.js index a29d2615c60..1a35186287c 100644 --- a/chromium/chrome/browser/resources/settings/device_page/display.js +++ b/chromium/chrome/browser/resources/settings/device_page/display.js @@ -80,6 +80,9 @@ Polymer({ notify: true, }, + /** Ids for mirroring destination displays. */ + mirroringDestinationIds: Array, + /** @private {!Array<number>} Mode index values for slider. */ modeValues_: Array, @@ -92,6 +95,14 @@ Polymer({ }, /** @private */ + multiMirroringAvailable_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('multiMirroringAvailable'); + } + }, + + /** @private */ nightLightFeatureEnabled_: { type: Boolean, value: function() { @@ -205,6 +216,10 @@ Polymer({ return; settings.display.systemDisplayApi.getDisplayLayout( this.displayLayoutFetched_.bind(this, displays)); + if (this.isMirrored_(displays)) + this.mirroringDestinationIds = displays[0].mirroringDestinationIds; + else + this.mirroringDestinationIds = []; }, /** @@ -256,16 +271,17 @@ Polymer({ }, /** - * Returns true if the given display has touch support and is not an internal - * display. If the feature is not enabled via the switch, this will return - * false. + * Returns true if external touch devices are connected and the current + * display is not an internal display. If the feature is not enabled via the + * switch, this will return false. * @param {!chrome.system.display.DisplayUnitInfo} display Display being * checked for touch support. * @return {boolean} * @private */ showTouchCalibrationSetting_: function(display) { - return !display.isInternal && display.hasTouchSupport && + return !display.isInternal && + loadTimeData.getBoolean('hasExternalTouchDevice') && loadTimeData.getBoolean('enableTouchCalibrationSetting'); }, @@ -310,7 +326,7 @@ Polymer({ * @private */ getDisplayMirrorText_: function(displays) { - return this.i18n(this.isMirrored_(displays) ? 'toggleOn' : 'toggleOff'); + return this.i18n('displayMirror', displays[0].name); }, /** @@ -344,7 +360,9 @@ Polymer({ */ showMirror_: function(unifiedDesktopMode, displays) { return this.isMirrored_(displays) || - (!unifiedDesktopMode && displays.length == 2); + (!unifiedDesktopMode && + ((this.multiMirroringAvailable_ && displays.length > 1) || + displays.length == 2)); }, /** @@ -496,7 +514,10 @@ Polymer({ }, /** @private */ - onMirroredTap_: function() { + onMirroredTap_: function(event) { + // Blur the control so that when the transition animation completes and the + // UI is focused, the control does not receive focus. crbug.com/785070 + event.target.blur(); var id = ''; /** @type {!chrome.system.display.DisplayProperties} */ var properties = {}; if (this.isMirrored_(this.displays)) { @@ -565,7 +586,8 @@ Polymer({ this.unifiedDesktopMode_ = !!primaryDisplay && primaryDisplay.isUnified; - this.$.displayLayout.updateDisplays(this.displays, this.layouts); + this.$.displayLayout.updateDisplays( + this.displays, this.layouts, this.mirroringDestinationIds); }, /** @private */ diff --git a/chromium/chrome/browser/resources/settings/device_page/display_layout.html b/chromium/chrome/browser/resources/settings/device_page/display_layout.html index d5a610f0db7..289ebf9c8f2 100644 --- a/chromium/chrome/browser/resources/settings/device_page/display_layout.html +++ b/chromium/chrome/browser/resources/settings/device_page/display_layout.html @@ -58,10 +58,11 @@ } </style> <div id="displayArea" on-iron-resize="calculateVisualScale_"> - <template is="dom-repeat" items="[[displays]]"> - <div id="_mirror_[[item.id]]" class="display mirror" + <template is="dom-repeat" items="[[mirroringDestinationIds_]]"> + <div id="_mirror_[[item]]" class="display mirror" hidden$="[[!mirroring]]" - style$="[[getMirrorDivStyle_(item.id, item.bounds, visualScale)]]"> + style$="[[getMirrorDivStyle_(index, mirroringDestinationIds_.length, + displays, visualScale)]]"> </div> </template> <template is="dom-repeat" items="[[displays]]"> @@ -69,7 +70,12 @@ draggable="[[dragEnabled]]" on-tap="onSelectDisplayTap_" style$="[[getDivStyle_(item.id, item.bounds, visualScale)]]" selected$="[[isSelected_(item, selectedDisplay)]]"> - [[item.name]] + <div hidden$="[[mirroring]]"> + [[item.name]] + </div> + <div hidden$="[[!mirroring]]"> + $i18n{displayMirrorDisplayName} + </div> </div> </template> </div> diff --git a/chromium/chrome/browser/resources/settings/device_page/display_layout.js b/chromium/chrome/browser/resources/settings/device_page/display_layout.js index 7063a7c0a80..c5ef6d6491d 100644 --- a/chromium/chrome/browser/resources/settings/device_page/display_layout.js +++ b/chromium/chrome/browser/resources/settings/device_page/display_layout.js @@ -36,6 +36,13 @@ Polymer({ * @type {number} */ visualScale: 1, + + /** + * Ids for mirroring destination displays. + * @type {!Array<string>|undefined} + * @private + */ + mirroringDestinationIds_: Array, }, /** @private {!{left: number, top: number}} */ @@ -51,10 +58,12 @@ Polymer({ * have been fetched from chrome. * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays * @param {!Array<!chrome.system.display.DisplayLayout>} layouts + * @param {!Array<string>} mirroringDestinationIds */ - updateDisplays: function(displays, layouts) { + updateDisplays: function(displays, layouts, mirroringDestinationIds) { this.displays = displays; this.layouts = layouts; + this.mirroringDestinationIds_ = mirroringDestinationIds; this.initializeDisplayLayout(displays, layouts); @@ -137,15 +146,15 @@ Polymer({ * @param {string} id * @param {!chrome.system.display.Bounds} displayBounds * @param {number} visualScale - * @param {boolean=} opt_mirrored + * @param {number=} opt_offset * @return {string} The style string for the div. * @private */ - getDivStyle_: function(id, displayBounds, visualScale, opt_mirrored) { + getDivStyle_: function(id, displayBounds, visualScale, opt_offset) { // This matches the size of the box-shadow or border in CSS. /** @const {number} */ var BORDER = 1; /** @const {number} */ var MARGIN = 4; - /** @const {number} */ var OFFSET = opt_mirrored ? -4 : 0; + /** @const {number} */ var OFFSET = opt_offset || 0; /** @const {number} */ var PADDING = 3; var bounds = this.getCalculatedDisplayBounds(id, true /* notest */); if (!bounds) @@ -163,14 +172,22 @@ Polymer({ }, /** - * @param {string} id - * @param {!chrome.system.display.Bounds} displayBounds + * @param {number} mirroringDestinationIndex + * @param {number} mirroringDestinationDisplayNum + * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays * @param {number} visualScale * @return {string} The style string for the mirror div. * @private */ - getMirrorDivStyle_: function(id, displayBounds, visualScale) { - return this.getDivStyle_(id, displayBounds, visualScale, true); + getMirrorDivStyle_: function( + mirroringDestinationIndex, mirroringDestinationDisplayNum, displays, + visualScale) { + // All destination displays have the same bounds as the mirroring source + // display, but we add a little offset to each destination display's bounds + // so that they can be distinguished from each other in the layout. + return this.getDivStyle_( + displays[0].id, displays[0].bounds, visualScale, + (mirroringDestinationDisplayNum - mirroringDestinationIndex) * -4); }, /** diff --git a/chromium/chrome/browser/resources/settings/device_page/stylus.html b/chromium/chrome/browser/resources/settings/device_page/stylus.html index 4c50c21298c..6a0dad02d98 100644 --- a/chromium/chrome/browser/resources/settings/device_page/stylus.html +++ b/chromium/chrome/browser/resources/settings/device_page/stylus.html @@ -1,5 +1,6 @@ <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/html/action_link.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"> @@ -29,13 +30,15 @@ } </style> - <settings-toggle-button class="continuation" + <settings-toggle-button id="enableStylusToolsToggle" + class="continuation" pref="{{prefs.settings.enable_stylus_tools}}" label="$i18n{stylusEnableStylusTools}"> </settings-toggle-button> <template is="dom-if" if="[[hasInternalStylus_]]"> <settings-toggle-button + id ="launchPaletteOnEjectEventToggle" pref="{{prefs.settings.launch_palette_on_eject_event}}" label="$i18n{stylusAutoOpenStylusTools}" disabled="[[!prefs.settings.enable_stylus_tools.value]]"> @@ -103,12 +106,12 @@ indicator-type="[[userPolicyIndicator_]]"> </cr-policy-indicator> </template> - <paper-toggle-button id="enable-app-on-lock-screen-toggle" + <cr-toggle id="enable-app-on-lock-screen-toggle" aria-labelledby="lock-screen-toggle-label" disabled="[[disallowedOnLockScreenByPolicy_(selectedApp_)]]" checked="[[lockScreenSupportEnabled_(selectedApp_)]]" on-change="toggleLockScreenSupport_"> - </paper-toggle-button> + </cr-toggle> </div> <template is="dom-if" if="[[lockScreenSupportEnabled_(selectedApp_)]]"> diff --git a/chromium/chrome/browser/resources/settings/icons.html b/chromium/chrome/browser/resources/settings/icons.html index 61224dceb7b..07119529659 100644 --- a/chromium/chrome/browser/resources/settings/icons.html +++ b/chromium/chrome/browser/resources/settings/icons.html @@ -54,6 +54,7 @@ List icons here rather than importing large sets of (e.g. Polymer) icons. <if expr="chromeos"> <g id="chevron-left"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"</path></g> </if> + <g id="clipboard"><path d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"></path></g> <g id="cloud"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"></path></g> <g id="code"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"></path></g> <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> @@ -64,7 +65,6 @@ List icons here rather than importing large sets of (e.g. Polymer) icons. </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> - <g id="error-outline"><path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"></path></g> <if expr="chromeos"> <g id="fingerprint"><path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5 9.72c-.1 0-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07c-.13 0-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96 0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"></path></g> <g id="gamepad"><path d="M15 7.5V2H9v5.5l3 3 3-3zM7.5 9H2v6h5.5l3-3-3-3zM9 16.5V22h6v-5.5l-3-3-3 3zM16.5 9l-3 3 3 3H22V9h-5.5z"></path></g> @@ -72,6 +72,7 @@ List icons here rather than importing large sets of (e.g. Polymer) icons. </if> <g id="help-outline"><path d="M11 18h2v-2h-2v2zm1-16C6.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 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"></path></g> <g id="info"><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-6h2v6zm0-8h-2V7h2v2z"></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> <if expr="chromeos"> <g id="keyboard"><path d="M20 5H4c-1.1 0-1.99.9-1.99 2L2 17c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z"></path></g> </if> @@ -81,9 +82,6 @@ List icons here rather than importing large sets of (e.g. Polymer) icons. </if> <g id="list"><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"></path></g> <g id="location-on"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"></path></g> -<if expr="chromeos"> - <g id="lock"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"></path></g> -</if> <g id="mic"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"></path></g> <g id="midi"><path d="M21,19.1H3V5h18V19.1z M21,3H3C1.9,3,1,3.9,1,5v14c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z"></path><path fill="none" d="M21,19.1H3V5h18V19.1z M21,3H3C1.9,3,1,3.9,1,5v14c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z"></path><path d="M14,5h3v8h-3V5z"></path><path d="M15,12h1v8h-1V12z"></path><path fill="none" d="M0,0h24v24H0V0z"></path><rect x="7" y="5" width="3" height="8"></rect><rect x="8" y="12" width="1" height="8"></rect></g> <if expr="chromeos"> @@ -109,7 +107,6 @@ List icons here rather than importing large sets of (e.g. Polymer) icons. <g id="signal-cellular-2-bar"><path fill-opacity=".3" d="M2 22h20V2z"></path><path d="M14 10L2 22h12z"></path></g> <g id="signal-cellular-3-bar"><path fill-opacity=".3" d="M2 22h20V2z"></path><path d="M17 7L2 22h15z"></path></g> <g id="signal-cellular-4-bar"><path d="M2 22h20V2z"></path></g> - <g id="sim-card-alert"><path d="M18 2h-8L4.02 8 4 20c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-5 15h-2v-2h2v2zm0-4h-2V8h2v5z"></path></g> <g id="smartphone"><path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"></path></g> </if> <g id="supervisor-account"><path d="M16.5 12c1.38 0 2.49-1.12 2.49-2.5S17.88 7 16.5 7C15.12 7 14 8.12 14 9.5s1.12 2.5 2.5 2.5zM9 11c1.66 0 2.99-1.34 2.99-3S10.66 5 9 5C7.34 5 6 6.34 6 8s1.34 3 3 3zm7.5 3c-1.83 0-5.5.92-5.5 2.75V19h11v-2.25c0-1.83-3.67-2.75-5.5-2.75zM9 13c-2.33 0-7 1.17-7 3.5V19h7v-2.25c0-.85.33-2.34 2.37-3.47C10.5 13.1 9.66 13 9 13z"></path></g> 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 18f9191b2ff..7ffb7c767f7 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/internet_page/compiled_resources2.gyp @@ -11,10 +11,20 @@ '<(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', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', '<(EXTERNS_GYP):chrome_send', '<(EXTERNS_GYP):management', '<(EXTERNS_GYP):networking_private', '<(INTERFACES_GYP):networking_private_interface', + 'internet_config', + 'internet_page_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'internet_page_browser_proxy', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -24,6 +34,7 @@ '../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/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', '<(EXTERNS_GYP):networking_private', @@ -43,6 +54,7 @@ '<(EXTERNS_GYP):networking_private', '<(INTERFACES_GYP):networking_private_interface', 'tether_connection_dialog', + 'internet_page_browser_proxy', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -68,6 +80,7 @@ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', '<(EXTERNS_GYP):networking_private', '<(INTERFACES_GYP):networking_private_interface', + 'internet_page_browser_proxy', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, @@ -86,17 +99,6 @@ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, { - 'target_name': 'network_siminfo', - 'dependencies': [ - '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-input/compiled_resources2.gyp:paper-input-extracted', - '<(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/cr/ui/compiled_resources2.gyp:focus_without_ink', - '<(INTERFACES_GYP):networking_private_interface', - ], - 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { 'target_name': 'network_summary', 'dependencies': [ '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', 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 fd8d716df89..c909581bc60 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_config.html +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_config.html @@ -1,52 +1,47 @@ <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/cr_elements/cr_dialog/cr_dialog.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="internet_shared_css.html"> -<dom-module id="settings-internet-config"> +<dom-module id="internet-config"> <template> <style include="internet-shared iron-flex"> + dialog { + width: 460px; + } </style> - <!-- Title section: Icon + name. --> - <div class="settings-box first"> - <div class="start layout horizontal center"> - <cr-network-icon network-state="[[networkProperties_]]" is-list-item> - </cr-network-icon> - <div class="title">[[getTitle_(networkProperties_.*)]]</div> + <dialog is="cr-dialog" id="dialog" close-text="$i18n{close}"> + <div slot="title">[[getDialogTitle_(networkProperties_)]]</div> + <div slot="body"> + <network-config id="networkConfig" class="flex" + networking-private="[[networkingPrivate]]" + global-policy="[[globalPolicy]]" + network-properties="{{networkProperties_}}" + enable-connect="{{enableConnect_}}" enable-save="{{enableSave_}}" + share-allow-enable="[[shareAllowEnable_]]" + share-default="[[shareDefault_]]" + on-close="close"> + </network-config> </div> - <div id="buttonDiv"> - <paper-button class="secondary-button" on-tap="onCancelTap_"> + + <div slot="button-container"> + <paper-button class="cancel-button" on-tap="onCancelTap_"> $i18n{cancel} </paper-button> - <template is="dom-if" if="[[guid_]]"> - <paper-button class="primary-button" on-tap="onSaveTap_" - disabled="[[!enableSave_]]"> - $i18n{save} - </paper-button> - </template> - <template is="dom-if" if="[[!guid_]]"> - <paper-button class="primary-button" on-tap="onConnectTap_" - disabled="[[!enableConnect_]]"> - $i18n{networkButtonConnect} - </paper-button> - </template> + <paper-button class="action-button" on-tap="onSaveOrConnectTap_" + disabled="[[!getSaveOrConnectEnabled_( + guid, networkProperties_, enableSave_, enableConnect_)]]"> + [[getSaveOrConnectLabel_(guid, networkProperties_)]] + </paper-button> </div> - </div> - <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> + </dialog> </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 a74aa471f2e..38b8d8961d5 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_config.js +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_config.js @@ -4,12 +4,12 @@ /** * @fileoverview - * 'settings-internet-config' is a Settings wrapper for network-config. + * 'internet-config' is a Settings dialog wrapper for network-config. */ Polymer({ - is: 'settings-internet-config', + is: 'internet-config', - behaviors: [settings.RouteObserverBehavior, I18nBehavior], + behaviors: [I18nBehavior], properties: { /** @@ -18,18 +18,49 @@ Polymer({ */ networkingPrivate: Object, + /** @type {!chrome.networkingPrivate.GlobalPolicy|undefined} */ + globalPolicy: Object, + + /** @private */ + shareAllowEnable_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('shareNetworkAllowEnable'); + } + }, + + /** @private */ + shareDefault_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('shareNetworkDefault'); + } + }, + /** * The GUID when an existing network is being configured. This will be * empty when configuring a new network. * @private */ - guid_: String, + guid: String, + + /** + * The type of network to be configured. + * @private {!chrome.networkingPrivate.NetworkType} + */ + type: String, + + /** + * The name of network (for display while the network details are fetched). + * @private + */ + name: String, /** @private */ - enableConnect_: String, + enableConnect_: Boolean, /** @private */ - enableSave_: String, + enableSave_: Boolean, /** * The current properties if an existing network is being configured, or @@ -40,69 +71,72 @@ Polymer({ networkProperties_: Object, }, - /** - * settings.RouteObserverBehavior - * @param {!settings.Route} route - * @protected - */ - currentRouteChanged: function(route) { - if (route != settings.routes.NETWORK_CONFIG) - return; - - var queryParams = settings.getQueryParameters(); - this.guid_ = queryParams.get('guid') || ''; + open: function() { + var dialog = /** @type {!CrDialogElement} */ (this.$.dialog); + if (!dialog.open) + dialog.showModal(); // Set networkProperties for new configurations and for existing // 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; - assert(type && type != CrOnc.Type.ALL); + assert(this.type && this.type != CrOnc.Type.ALL); this.networkProperties_ = { - GUID: this.guid_, - Name: name, - Type: type, + GUID: this.guid, + Name: this.name, + Type: this.type, }; - - // 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(); + close: function() { + var dialog = /** @type {!CrDialogElement} */ (this.$.dialog); + if (dialog.open) + dialog.close(); }, - /** @private */ - close_: function() { - if (settings.getCurrentRoute() == settings.routes.NETWORK_CONFIG) - settings.navigateToPreviousRoute(); + /** + * @return {string} + * @private + */ + getDialogTitle_: function() { + var name = this.networkProperties_.Name; + if (name) + return this.i18n('internetConfigName', name); + var type = this.i18n('OncType' + this.networkProperties_.Type); + return this.i18n('internetJoinType', type); + }, + + /** + * @return {boolean} + * @private + */ + isConfigured_: function() { + var source = this.networkProperties_.Source; + return !!this.guid && !!source && source != CrOnc.Source.NONE; }, /** * @return {string} * @private */ - getTitle_: function() { - return this.networkProperties_.Name || - this.i18n('OncType' + this.networkProperties_.Type); + getSaveOrConnectLabel_: function() { + return this.i18n(this.isConfigured_() ? 'save' : 'networkButtonConnect'); }, - /** @private */ - onCancelTap_: function() { - this.close_(); + /** + * @return {boolean} + * @private + */ + getSaveOrConnectEnabled_: function() { + return this.isConfigured_() ? this.enableSave_ : this.enableConnect_; }, /** @private */ - onSaveTap_: function() { - this.$.networkConfig.saveOrConnect(); + onCancelTap_: function() { + this.close(); }, /** @private */ - onConnectTap_: function() { + onSaveOrConnectTap_: function() { 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 ac1652041e5..67fa0ae5221 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 @@ -5,6 +5,7 @@ <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"> +<link rel="import" href="chrome://resources/cr_components/chromeos/network/network_siminfo.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/cr_elements/cr_expand_button/cr_expand_button.html"> @@ -23,7 +24,6 @@ <link rel="import" href="../route.html"> <link rel="import" href="internet_shared_css.html"> <link rel="import" href="network_proxy_section.html"> -<link rel="import" href="network_siminfo.html"> <link rel="import" href="tether_connection_dialog.html"> <dom-module id="settings-internet-detail-page"> @@ -96,7 +96,7 @@ <paper-button class="primary-button" on-tap="onConnectTap_" hidden$="[[!showConnect_(networkProperties, globalPolicy)]]" disabled="[[!enableConnect_(networkProperties, defaultNetwork, - globalPolicy)]]"> + globalPolicy, networkPropertiesReceived_)]]"> $i18n{networkButtonConnect} </paper-button> <paper-button class="primary-button" on-tap="onDisconnectTap_" @@ -158,15 +158,6 @@ </paper-toggle-button> </div> </template> - <!-- Choose Mobile Network (Cellular only). --> - <template is="dom-if" - if="[[showCellularChooseNetwork_(networkProperties)]]"> - <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" @@ -245,6 +236,15 @@ <iron-collapse opened="[[networkExpanded_]]"> <div class="settings-box single-column stretch indented"> + <!-- Choose Mobile Network (Cellular only). --> + <template is="dom-if" + if="[[showCellularChooseNetwork_(networkProperties)]]"> + <network-choose-mobile + networking-private="[[networkingPrivate]]" + network-properties="[[networkProperties]]"> + </network-choose-mobile> + </template> + <!-- APN --> <template is="dom-if" if="[[isCellular_(networkProperties)]]"> <network-apnlist editable on-apn-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 03ebf640485..24b95396a3a 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 @@ -120,12 +120,10 @@ Polymer({ proxyExpanded_: Boolean, }, - /** - * Listener function for chrome.networkingPrivate.onNetworksChanged event. - * @type {?function(!Array<string>)} - * @private - */ - networksChangedListener_: null, + listeners: { + 'network-list-changed': 'checkNetworkExists_', + 'networks-changed': 'updateNetworkDetails_', + }, /** @private {boolean} */ didSetFocus_: false, @@ -133,6 +131,8 @@ Polymer({ /** * Set to true to once the initial properties have been received. This * prevents setProperties from being called when setting default properties. + * This will also be set to false if the network no longer exists in the + * list of networks (e.g. it goes out of range). * @private {boolean} */ networkPropertiesReceived_: false, @@ -145,6 +145,14 @@ Polymer({ */ shouldShowConfigureWhenNetworkLoaded_: false, + /** @private {settings.InternetPageBrowserProxy} */ + browserProxy_: null, + + /** @override */ + created: function() { + this.browserProxy_ = settings.InternetPageBrowserProxyImpl.getInstance(); + }, + /** * settings.RouteObserverBehavior * @param {!settings.Route} route @@ -152,19 +160,9 @@ Polymer({ * @protected */ currentRouteChanged: function(route, oldRoute) { - if (route != settings.routes.NETWORK_DETAIL) { - if (this.networksChangedListener_) { - this.networkingPrivate.onNetworksChanged.removeListener( - this.networksChangedListener_); - this.networksChangedListener_ = null; - } + if (route != settings.routes.NETWORK_DETAIL) return; - } - if (!this.networksChangedListener_) { - this.networksChangedListener_ = this.onNetworksChangedEvent_.bind(this); - this.networkingPrivate.onNetworksChanged.addListener( - this.networksChangedListener_); - } + var queryParams = settings.getQueryParameters(); this.guid = queryParams.get('guid') || ''; if (!this.guid) { @@ -193,10 +191,9 @@ Polymer({ /** @private */ close_: function() { + this.guid = ''; // Delay navigating to allow other subpages to load first. - requestAnimationFrame(function() { - settings.navigateToPreviousRoute(); - }); + requestAnimationFrame(() => settings.navigateToPreviousRoute()); }, /** @private */ @@ -263,11 +260,20 @@ Polymer({ }, /** - * networkingPrivate.onNetworksChanged event callback. - * @param {!Array<string>} networkIds The list of changed network GUIDs. + * @param {{detail: !Array<string>}} event + * @private + */ + checkNetworkExists_: function(event) { + var networkIds = event.detail; + this.networkPropertiesReceived_ = networkIds.indexOf(this.guid) != -1; + }, + + /** + * @param {{detail: !Array<string>}} event * @private */ - onNetworksChangedEvent_: function(networkIds) { + updateNetworkDetails_: function(event) { + var networkIds = event.detail; if (networkIds.indexOf(this.guid) != -1) this.getNetworkDetails_(); }, @@ -305,11 +311,23 @@ Polymer({ this.close_(); return; } + + // Details page was closed while request was in progress, ignore the result. + if (!this.guid) + return; + if (!properties) { console.error('No properties for: ' + this.guid); this.close_(); return; } + + // Detail page should not be shown when Arc VPN is not connected. + if (this.isArcVpn_(properties) && !this.isConnectedState_(properties)) { + this.guid = ''; + this.close_(); + } + this.networkProperties = properties; this.networkPropertiesReceived_ = true; }, @@ -435,6 +453,11 @@ Polymer({ showConnect_: function(networkProperties, globalPolicy) { if (this.connectNotAllowed_(networkProperties, globalPolicy)) return false; + // TODO(lgcheng@) support connect Arc VPN from UI once Android support API + // to initiate a VPN session. + if (this.isArcVpn_(networkProperties)) + return false; + return networkProperties.Type != CrOnc.Type.ETHERNET && networkProperties.ConnectionState == CrOnc.ConnectionState.NOT_CONNECTED; @@ -460,6 +483,8 @@ Polymer({ var type = networkProperties.Type; if (type != CrOnc.Type.WI_FI && type != CrOnc.Type.VPN) return false; + if (this.isArcVpn_(networkProperties)) + return false; return !this.isPolicySource(networkProperties.Source) && this.isRemembered_(networkProperties); }, @@ -494,6 +519,10 @@ Polymer({ CrOnc.ConnectionState.NOT_CONNECTED) { return false; } + if (this.isArcVpn_(networkProperties) && + !this.isConnectedState_(networkProperties)) { + return false; + } return true; }, @@ -535,12 +564,17 @@ Polymer({ * @param {!CrOnc.NetworkProperties} networkProperties * @param {?CrOnc.NetworkStateProperties} defaultNetwork * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} networkPropertiesReceived * @return {boolean} Whether or not to enable the network connect button. * @private */ - enableConnect_: function(networkProperties, defaultNetwork, globalPolicy) { + enableConnect_: function( + networkProperties, defaultNetwork, globalPolicy, + networkPropertiesReceived) { if (!this.showConnect_(networkProperties, globalPolicy)) return false; + if (!networkPropertiesReceived) + return false; if ((networkProperties.Type == CrOnc.Type.CELLULAR) && (CrOnc.isSimLocked(networkProperties) || this.get('Cellular.Scanning', networkProperties))) { @@ -597,6 +631,11 @@ Polymer({ /** @private */ onConfigureTap_: function() { + if (this.networkProperties && this.isArcVpn_(this.networkProperties)) { + this.browserProxy_.showNetworkConfigure(this.guid); + return; + } + if (loadTimeData.getBoolean('networkSettingsConfig')) this.fire('show-config', this.networkProperties); else @@ -788,7 +827,8 @@ Polymer({ */ showAutoConnect_: function(networkProperties) { return networkProperties.Type != CrOnc.Type.ETHERNET && - this.isRemembered_(networkProperties); + this.isRemembered_(networkProperties) && + !this.isArcVpn_(networkProperties); }, /** @@ -824,7 +864,8 @@ Polymer({ showPreferNetwork_: function(networkProperties) { // TODO(stevenjb): Resolve whether or not we want to allow "preferred" for // networkProperties.Type == CrOnc.Type.ETHERNET. - return this.isRemembered_(networkProperties); + return this.isRemembered_(networkProperties) && + !this.isArcVpn_(networkProperties); }, /** @@ -868,6 +909,8 @@ Polymer({ var vpnType = CrOnc.getActiveValue(this.networkProperties.VPN.Type); if (vpnType == 'ThirdPartyVPN') { fields.push('VPN.ThirdPartyVPN.ProviderName'); + } else if (vpnType == 'ARCVPN') { + fields.push('VPN.Type'); } else { fields.push('VPN.Host', 'VPN.Type'); if (vpnType == 'OpenVPN') @@ -952,7 +995,8 @@ Polymer({ return false; } return this.hasAdvancedFields_() || this.hasDeviceFields_() || - this.isRememberedOrConnected_(networkProperties); + (networkProperties.Type != CrOnc.Type.VPN && + this.isRememberedOrConnected_(networkProperties)); }, /** @@ -1019,12 +1063,29 @@ Polymer({ }, /** + * @param {!CrOnc.NetworkProperties} networkProperties + * @return {boolean} + * @private + */ + isArcVpn_: function(networkProperties) { + return !!networkProperties.VPN && + CrOnc.getActiveValue(networkProperties.VPN.Type) == 'ARCVPN'; + }, + + /** * @param {string} ipAddress * @param {!CrOnc.NetworkProperties} networkProperties * @return {boolean} * @private */ showIpAddress_: function(ipAddress, networkProperties) { + // Arc Vpn does not currently pass IP configuration to ChromeOS. IP address + // property holds an internal IP address Android uses to talk to ChromeOS. + // TODO(lgcheng@) Show correct IP address when we implement IP configuration + // correclty. + if (this.isArcVpn_(networkProperties)) + return false; + return !!ipAddress && this.isConnectedState_(networkProperties); }, diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js b/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js index 5ec39d7469b..b1dc5e6e712 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js @@ -54,44 +54,17 @@ Polymer({ enableForget_: Boolean, }, + listeners: {'network-list-changed': 'refreshNetworks_'}, + /** @private {string} */ selectedGuid_: '', - /** - * Listener function for chrome.networkingPrivate.onNetworksChanged event. - * @type {function(!Array<string>)} - * @private - */ - networksChangedListener_: function() {}, - - /** @override */ - attached: function() { - this.networksChangedListener_ = this.onNetworksChangedEvent_.bind(this); - this.networkingPrivate.onNetworksChanged.addListener( - this.networksChangedListener_); - }, - - /** @override */ - detached: function() { - this.networkingPrivate.onNetworksChanged.removeListener( - this.networksChangedListener_); - }, - /** @private */ networkTypeChanged_: function() { this.refreshNetworks_(); }, /** - * networkingPrivate.onNetworksChanged event callback. - * @param {!Array<string>} networkIds The list of changed network GUIDs. - * @private - */ - onNetworksChangedEvent_: function(networkIds) { - this.refreshNetworks_(); - }, - - /** * Requests the list of network states from Chrome. Updates networkStates * once the results are returned from Chrome. * @private 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 f77ea978261..cd6b2afbadb 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_page.html +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_page.html @@ -4,6 +4,7 @@ <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.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/neon-animation/neon-animatable.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> @@ -73,6 +74,15 @@ </button> </div> </template> + <template is="dom-if" if="[[arcVpnProviders_.length]]"> + <div actionable class="list-item" id="addArcVpn" + on-tap="onAddArcVpnTap_"> + <div class="start">$i18n{internetAddArcVPN}</div> + <button class="icon-external" is="paper-icon-button-light" + aria-label$="$i18n{internetAddArcVPN}"> + </button> + </div> + </template> </div> </template> </template> @@ -84,14 +94,7 @@ </template> </neon-animatable> - <template is="dom-if" route-path="/networkConfig" no-search> - <settings-subpage page-title="$i18n{internetConfigTitle}"> - <settings-internet-config networking-private="[[networkingPrivate]]"> - </settings-internet-config> - </settings-subpage> - </template> - - <template is="dom-if" route-path="/networkDetail" no-search> + <template is="dom-if" route-path="/networkDetail" no-search restamp> <settings-subpage page-title="$i18n{internetDetailPageTitle}"> <settings-internet-detail-page prefs="{{prefs}}" default-network="[[defaultNetwork]]" @@ -101,7 +104,7 @@ </settings-subpage> </template> - <template is="dom-if" route-path="/knownNetworks" no-search> + <template is="dom-if" route-path="/knownNetworks" no-search restamp> <settings-subpage page-title="$i18n{internetKnownNetworksPageTitle}"> <settings-internet-known-networks-page network-type="[[knownNetworksType_]]" @@ -110,7 +113,7 @@ </settings-subpage> </template> - <template is="dom-if" route-path="/networks" no-search> + <template is="dom-if" route-path="/networks" no-search restamp> <settings-subpage page-title="[[getNetworksPageTitle_(subpageType_)]]" show-spinner="[[showSpinner_]]"> <settings-internet-subpage @@ -119,6 +122,7 @@ tether-device-state="[[get('Tether', deviceStates)]]" global-policy="[[globalPolicy_]]" third-party-vpn-providers="[[thirdPartyVpnProviders_]]" + arc-vpn-providers="[[arcVpnProviders_]]" networking-private="[[networkingPrivate]]" show-spinner="{{showSpinner_}}"> </settings-bluetooth-subpage> @@ -126,6 +130,12 @@ </template> </settings-animated-pages> + + <internet-config id="configDialog" + networking-private="[[networkingPrivate]]" + global-policy="[[globalPolicy_]]"> + </internet-config> + </template> <script src="internet_page.js"></script> </dom-module> 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 85c7a78c1c8..8e935fa68cf 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_page.js +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_page.js @@ -10,7 +10,8 @@ Polymer({ is: 'settings-internet-page', - behaviors: [I18nBehavior, settings.RouteObserverBehavior], + behaviors: + [I18nBehavior, settings.RouteObserverBehavior, WebUIListenerBehavior], properties: { /** @@ -89,6 +90,18 @@ Polymer({ } }, + /** + * List of Arc VPN providers. + * @type {!Array<!settings.ArcVpnProvider>} + * @private + */ + arcVpnProviders_: { + type: Array, + value: function() { + return []; + } + }, + /** @private {!Map<string, string>} */ focusConfig_: { type: Object, @@ -112,6 +125,13 @@ Polymer({ }, // chrome.networkingPrivate listeners + /** @private {?function(!Array<string>)} */ + networkListChangedListener_: null, + + /** @private {?function(!Array<string>)} */ + networksChangedListener_: null, + + // chrome.management listeners /** @private {Function} */ onExtensionAddedListener_: null, @@ -121,8 +141,33 @@ Polymer({ /** @private {Function} */ onExtensionDisabledListener_: null, + /** @private {settings.InternetPageBrowserProxy} */ + browserProxy_: null, + + /** @override */ + created: function() { + this.browserProxy_ = settings.InternetPageBrowserProxyImpl.getInstance(); + }, + + /** @override */ + ready: function() { + this.browserProxy_.setUpdateArcVpnProvidersCallback( + this.onArcVpnProvidersReceived_.bind(this)); + this.browserProxy_.requestArcVpnProviders(); + }, + /** @override */ attached: function() { + this.networkListChangedListener_ = this.networkListChangedListener_ || + this.onNetworkListChanged_.bind(this); + this.networkingPrivate.onNetworkListChanged.addListener( + this.networkListChangedListener_); + + this.networksChangedListener_ = + this.networksChangedListener_ || this.onNetworksChanged_.bind(this); + this.networkingPrivate.onNetworksChanged.addListener( + this.networksChangedListener_); + this.onExtensionAddedListener_ = this.onExtensionAddedListener_ || this.onExtensionAdded_.bind(this); chrome.management.onInstalled.addListener(this.onExtensionAddedListener_); @@ -146,6 +191,11 @@ Polymer({ /** @override */ detached: function() { + this.networkingPrivate.onNetworkListChanged.removeListener( + assert(this.networkListChangedListener_)); + this.networkingPrivate.onNetworksChanged.removeListener( + assert(this.networksChangedListener_)); + chrome.management.onInstalled.removeListener( assert(this.onExtensionAddedListener_)); chrome.management.onEnabled.removeListener( @@ -233,13 +283,13 @@ Polymer({ * @private */ showConfig_: function(type, guid, name) { - var params = new URLSearchParams; - params.append('type', type); - if (guid) - params.append('guid', guid); - if (name) - params.append('name', name); - settings.navigateTo(settings.routes.NETWORK_CONFIG, params); + var configDialog = + /** @type {!InternetConfigElement} */ (this.$.configDialog); + configDialog.type = + /** @type {chrome.networkingPrivate.NetworkType} */ (type); + configDialog.guid = guid || ''; + configDialog.name = name || ''; + configDialog.open(); }, /** @@ -261,11 +311,7 @@ Polymer({ * @private */ onShowNetworks_: function(event) { - this.detailType_ = event.detail.Type; - var params = new URLSearchParams; - params.append('type', event.detail.Type); - this.subpageType_ = event.detail.Type; - settings.navigateTo(settings.routes.INTERNET_NETWORKS, params); + this.showNetworksSubpage_(event.detail.Type); }, /** @@ -353,7 +399,24 @@ Polymer({ */ onAddThirdPartyVpnTap_: function(event) { var provider = event.model.item; - chrome.send('addNetwork', [CrOnc.Type.VPN, provider.ExtensionID]); + this.browserProxy_.addThirdPartyVpn(CrOnc.Type.VPN, provider.ExtensionID); + }, + + /** @private */ + onAddArcVpnTap_: function() { + this.showNetworksSubpage_(CrOnc.Type.VPN); + }, + + /** + * @param {string} type + * @private + */ + showNetworksSubpage_: function(type) { + this.detailType_ = type; + var params = new URLSearchParams; + params.append('type', type); + this.subpageType_ = type; + settings.navigateTo(settings.routes.INTERNET_NETWORKS, params); }, /** @@ -393,6 +456,45 @@ Polymer({ }, /** + * This event is triggered when the list of networks changes. + * |networkIds| contains the ids for all visible or configured networks. + * networkingPrivate.onNetworkListChanged event callback. + * @param {!Array<string>} networkIds + * @private + */ + onNetworkListChanged_: function(networkIds) { + var event = new CustomEvent('network-list-changed', {detail: networkIds}); + this.maybeDispatchEvent_('network-summary', event); + this.maybeDispatchEvent_('settings-internet-detail-page', event); + this.maybeDispatchEvent_('settings-internet-known-networks-page', event); + this.maybeDispatchEvent_('settings-internet-subpage', event); + }, + + /** + * This event is triggered when interesting properties of a network change. + * |networkIds| contains the ids for networks whose properties have changed. + * networkingPrivate.onNetworksChanged event callback. + * @param {!Array<string>} networkIds + * @private + */ + onNetworksChanged_: function(networkIds) { + var event = new CustomEvent('networks-changed', {detail: networkIds}); + this.maybeDispatchEvent_('network-summary', event); + this.maybeDispatchEvent_('settings-internet-detail-page', event); + }, + + /** + * @param {!Event} event + * @private + */ + maybeDispatchEvent_: function(identifier, event) { + var element = this.$$(identifier); + if (!element) + return; + element.dispatchEvent(event); + }, + + /** * chrome.management.onInstalled or onEnabled event. * @param {!chrome.management.ExtensionInfo} extension * @private @@ -417,6 +519,29 @@ Polymer({ }, /** + * Compares Arc VPN Providers based on LastlauchTime + * @param {!settings.ArcVpnProvider} arcVpnProvider1 + * @param {!settings.ArcVpnProvider} arcVpnProvider2 + * @private + */ + compareArcVpnProviders_: function(arcVpnProvider1, arcVpnProvider2) { + if (arcVpnProvider1.LastLaunchTime > arcVpnProvider2.LastLaunchTime) + return -1; + if (arcVpnProvider1.LastLaunchTime < arcVpnProvider2.LastLaunchTime) + return 1; + return 0; + }, + + /** + * @param {?Array<!settings.ArcVpnProvider>} arcVpnProviders + * @private + */ + onArcVpnProvidersReceived_: function(arcVpnProviders) { + arcVpnProviders.sort(this.compareArcVpnProviders_); + this.arcVpnProviders_ = arcVpnProviders; + }, + + /** * chrome.management.onDisabled event. * @param {{id: string}} extension * @private diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_page_browser_proxy.html b/chromium/chrome/browser/resources/settings/internet_page/internet_page_browser_proxy.html new file mode 100644 index 00000000000..5989a2fed95 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_page_browser_proxy.html @@ -0,0 +1,2 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="internet_page_browser_proxy.js"></script> diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_page_browser_proxy.js b/chromium/chrome/browser/resources/settings/internet_page/internet_page_browser_proxy.js new file mode 100644 index 00000000000..80cce5c9341 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_page_browser_proxy.js @@ -0,0 +1,78 @@ +// 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 for Internet page. */ +cr.exportPath('settings'); + +/** + * @typedef {{ + * PackageName: string, + * ProviderName: string, + * AppID: string, + * LastLaunchTime: number, + * }} + */ +settings.ArcVpnProvider; + +cr.define('settings', function() { + /** @interface */ + class InternetPageBrowserProxy { + /** + * Shows configuration of connnected external VPN network. + * @param {string} guid + */ + showNetworkConfigure(guid) {} + + /** + * Sends add VPN request to external VPN provider. + * @param {string} networkType + * @param {string} appId + */ + addThirdPartyVpn(networkType, appId) {} + + /** + * Requests Chrome to send list of Arc VPN providers. + */ + requestArcVpnProviders() {} + + /** + * |callback| is run when there is update of Arc VPN providers. + * Available after |requestArcVpnProviders| has been called. + * @param {function(?Array<settings.ArcVpnProvider>):void} callback + */ + setUpdateArcVpnProvidersCallback(callback) {} + } + + /** + * @implements {settings.InternetPageBrowserProxy} + */ + class InternetPageBrowserProxyImpl { + /** @override */ + showNetworkConfigure(guid) { + chrome.send('configureNetwork', [guid]); + } + + /** @override */ + addThirdPartyVpn(networkType, appId) { + chrome.send('addNetwork', [networkType, appId]); + } + + /** @override */ + requestArcVpnProviders() { + chrome.send('requestArcVpnProviders'); + } + + /** @override */ + setUpdateArcVpnProvidersCallback(callback) { + cr.addWebUIListener('sendArcVpnProviders', callback); + } + } + + cr.addSingletonGetter(InternetPageBrowserProxyImpl); + + return { + InternetPageBrowserProxy: InternetPageBrowserProxy, + InternetPageBrowserProxyImpl: InternetPageBrowserProxyImpl, + }; +}); 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 1d60703ee2c..3b367ab31c3 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 @@ -1,4 +1,5 @@ <link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="internet_page_browser_proxy.html"> <!-- Common styles for Internet settings. --> <dom-module id="internet-shared"> diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html index 3d0116aba06..892b52e6555 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html @@ -102,8 +102,8 @@ class="no-networks"> </div> - <!-- Third party VPNs. --> <template is="dom-if" if="[[isEqual_('VPN', deviceState.Type)]]"> + <!-- Third party VPNs. --> <template is="dom-repeat" items="[[thirdPartyVpnProviders]]"> <div id="[[item.ProviderName]]" class="vpn-header layout horizontal center"> @@ -123,6 +123,26 @@ $i18n{internetNoNetworks} </div> </template> + <!-- Arc VPNs. --> + <template is="dom-repeat" items="[[arcVpnProviders]]"> + <div id="[[item.ProviderName]]" + class="vpn-header layout horizontal center"> + <div class="flex">[[item.ProviderName]]</div> + <button is="paper-icon-button-light" class="icon-add-circle" + aria-label$="[[getAddArcVpnAllyString_(item)]]" + on-tap="onAddArcVpnTap_" tabindex$="[[tabindex]]"> + </button> + </div> + <cr-network-list show-buttons + hidden$="[[!haveArcVpnNetwork_(arcVpns_, item)]]" + networks="[[getArcVpnNetworks_(arcVpns_, item)]]" + on-selected="onNetworkSelected_"> + </cr-network-list> + <div hidden$="[[haveArcVpnNetwork_(arcVpns_, item)]]" + class="no-networks"> + $i18n{internetNoNetworks} + </div> + </template> </template> </div> 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 0434b0e14c0..69592e2c91b 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js @@ -50,6 +50,12 @@ Polymer({ thirdPartyVpnProviders: Array, /** + * List of Arc VPN providers. + * @type {!Array<!settings.ArcVpnProvider>|undefined} + */ + arcVpnProviders: Array, + + /** * Interface for networkingPrivate calls, passed from internet_page. * @type {!NetworkingPrivate} */ @@ -82,33 +88,37 @@ Polymer({ return {}; }, }, + + /** + * Dictionary of lists of network states for Arc VPNs. + * @private {!Object<!Array<!CrOnc.NetworkStateProperties>>} + */ + arcVpns_: { + type: Object, + value: function() { + return {}; + } + } }, + listeners: {'network-list-changed': 'getNetworkStateList_'}, + observers: ['deviceStateChanged_(networkingPrivate, deviceState)'], /** @private {number|null} */ scanIntervalId_: null, - /** - * Listener function for chrome.networkingPrivate.onNetworkListChanged event. - * @type {?function(!Array<string>)} - * @private - */ - networkListChangedListener_: null, + /** @private {settings.InternetPageBrowserProxy} */ + browserProxy_: null, - /** override */ - attached: function() { - this.networkListChangedListener_ = this.networkListChangedListener_ || - this.onNetworkListChangedEvent_.bind(this); - this.networkingPrivate.onNetworkListChanged.addListener( - this.networkListChangedListener_); + /** @override */ + created: function() { + this.browserProxy_ = settings.InternetPageBrowserProxyImpl.getInstance(); }, /** override */ detached: function() { this.stopScanning_(); - this.networkingPrivate.onNetworkListChanged.removeListener( - assert(this.networkListChangedListener_)); }, /** @@ -124,6 +134,7 @@ Polymer({ // Clear any stale data. this.networkStateList_ = []; this.thirdPartyVpns_ = {}; + this.arcVpns_ = {}; // Request the list of networks and start scanning if necessary. this.getNetworkStateList_(); this.updateScanning_(); @@ -195,14 +206,6 @@ Polymer({ this.scanIntervalId_ = null; }, - /** - * networkingPrivate.onNetworkListChanged event callback. - * @private - */ - onNetworkListChangedEvent_: function() { - this.getNetworkStateList_(); - }, - /** @private */ getNetworkStateList_: function() { if (!this.deviceState) @@ -237,23 +240,30 @@ Polymer({ return; } - // For VPNs, separate out third party VPNs. + // For VPNs, separate out third party VPNs and Arc VPNs. if (this.deviceState.Type == CrOnc.Type.VPN) { var builtinNetworkStates = []; var thirdPartyVpns = {}; + var arcVpns = {}; for (var i = 0; i < networkStates.length; ++i) { var state = networkStates[i]; - var providerType = state.VPN && state.VPN.ThirdPartyVPN && - state.VPN.ThirdPartyVPN.ProviderName; + var providerType = this.get('VPN.ThirdPartyVPN.ProviderName', state); if (providerType) { thirdPartyVpns[providerType] = thirdPartyVpns[providerType] || []; thirdPartyVpns[providerType].push(state); + } else if (this.get('VPN.Type', state) == 'ARCVPN') { + var arcProviderName = this.get('VPN.Host', state); + if (state.ConnectionState != CrOnc.ConnectionState.CONNECTED) + continue; + arcVpns[arcProviderName] = arcVpns[arcProviderName] || []; + arcVpns[arcProviderName].push(state); } else { builtinNetworkStates.push(state); } } networkStates = builtinNetworkStates; this.thirdPartyVpns_ = thirdPartyVpns; + this.arcVpns_ = arcVpns; } this.networkStateList_ = networkStates; @@ -329,6 +339,15 @@ Polymer({ }, /** + * @param {!settings.ArcVpnProvider} arcVpn + * @return {string} + * @private + */ + getAddArcVpnAllyString_: function(arcVpn) { + return this.i18n('internetAddArcVPNProvider', arcVpn.ProviderName); + }, + + /** * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy * @return {boolean} * @private @@ -370,7 +389,19 @@ Polymer({ */ onAddThirdPartyVpnTap_: function(event) { var provider = event.model.item; - chrome.send('addNetwork', [CrOnc.Type.VPN, provider.ExtensionID]); + this.browserProxy_.addThirdPartyVpn(CrOnc.Type.VPN, provider.ExtensionID); + }, + + /** + * @param {!{model: + * !{item: !settings.ArcVpnProvider}, + * }} event + * @private + */ + onAddArcVpnTap_: function(event) { + var provider = event.model.item; + settings.InternetPageBrowserProxyImpl.getInstance().addThirdPartyVpn( + CrOnc.Type.VPN, provider.AppID); }, /** @@ -428,6 +459,27 @@ Polymer({ }, /** + * @param {!Object<!Array<!CrOnc.NetworkStateProperties>>} arcVpns + * @param {!settings.ArcVpnProvider} arcVpnProvider + * @return {!Array<!CrOnc.NetworkStateProperties>} + * @private + */ + getArcVpnNetworks_: function(arcVpns, arcVpnProvider) { + return arcVpns[arcVpnProvider.PackageName] || []; + }, + + /** + * @param {!Object<!Array<!CrOnc.NetworkStateProperties>>} arcVpns + * @param {!settings.ArcVpnProvider} arcVpnProvider + * @return {boolean} + * @private + */ + haveArcVpnNetwork_: function(arcVpns, arcVpnProvider) { + var list = this.getArcVpnNetworks_(arcVpns, arcVpnProvider); + return !!list.length; + }, + + /** * Event triggered when a network list item is selected. * @param {!{target: HTMLElement, detail: !CrOnc.NetworkStateProperties}} e * @private diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_siminfo.html b/chromium/chrome/browser/resources/settings/internet_page/network_siminfo.html deleted file mode 100644 index 8f4df0be364..00000000000 --- a/chromium/chrome/browser/resources/settings/internet_page/network_siminfo.html +++ /dev/null @@ -1,199 +0,0 @@ -<link rel="import" href="chrome://resources/html/polymer.html"> - -<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> -<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.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/polymer/v1_0/iron-a11y-keys/iron-a11y-keys.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/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-input/paper-input.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> -<link rel="import" href="../icons.html"> -<link rel="import" href="internet_shared_css.html"> - -<dom-module id="network-siminfo"> - <template> - <style include="settings-shared internet-shared iron-flex"> - iron-icon { - -webkit-margin-end: 10px; - } - - .dialog-error { - color: red; - font-size: 125%; - font-weight: 500; - margin-top: 10px; - } - - .error { - color: red; - font-weight: 500; - } - - .pin { - min-width: 100px; - } - - .puk { - min-width: 200px; - } - - /* Siminfo is embedded; remove the padding. */ - .settings-box { - padding: 0; - } - - .settings-box:first-of-type { - border-top: none; - } - - paper-toggle-button { - -webkit-margin-start: var(--settings-control-label-spacing); - } - </style> - - <!-- SIM missing UI --> - <div class="settings-box two-line" - hidden$="[[networkProperties.Cellular.SIMPresent]]"> - <div class="start layout horizontal center"> - <iron-icon icon="settings:sim-card-alert"></iron-icon> - <div class="error">$i18n{networkSimCardMissing}</div> - </div> - </div> - - <!-- SIM locked --> - <div class="settings-box two-line" - hidden$="[[!showSimLocked_(networkProperties)]]"> - <div class="start layout horizontal center"> - <iron-icon icon="settings:lock"></iron-icon> - <div class="error">$i18n{networkSimCardLocked}</div> - </div> - <div class="separator"></div> - <paper-button id="unlockPinButton" on-tap="onUnlockPinTap_"> - $i18n{networkSimUnlock} - </paper-button> - </div> - - <!-- SIM unlocked --> - <div class="settings-box two-line" - hidden$="[[!showSimUnlocked_(networkProperties)]]"> - <div id="simLockToggleLabel" class="start"> - $i18n{networkSimLockEnable} - </div> - <paper-button id="changePinButton" on-tap="onChangePinTap_" - hidden$="[[!networkProperties.Cellular.SIMLockStatus.LockEnabled]]"> - $i18n{networkSimChangePin} - </paper-button> - <paper-toggle-button id="simLockButton" - on-change="onSimLockEnabledChange_" checked="{{lockEnabled_}}" - aria-labelledby="simLockToggleLabel"> - </paper-toggle-button> - </div> - - <!-- Enter PIN dialog --> - <dialog is="cr-dialog" id="enterPinDialog" close-text="$i18n{close}" - on-cancel="onEnterPinDialogCancel_" - on-close="onEnterPinDialogClose_"> - <div slot="title">$i18n{networkSimEnterPinTitle}</div> - <div slot="body"> - <paper-input id="enterPin" class="pin" no-label-float autofocus - label="$i18n{networkSimEnterPin}"> - <iron-a11y-keys keys="enter" on-keys-pressed="sendEnterPin_"> - </iron-a11y-keys> - </paper-input> - <div class="dialog-error"> - [[getErrorMsg_(error_, networkProperties)]] - </div> - </div> - <div slot="button-container"> - <paper-button on-tap="sendEnterPin_"> - $i18n{networkSimEnter} - </paper-button> - </div> - </dialog> - - <!-- Change PIN dialog --> - <dialog is="cr-dialog" id="changePinDialog" close-text="$i18n{close}" - on-close="onChangePinDialogClose_"> - <div slot="title">$i18n{networkSimChangePinTitle}</div> - <div slot="body"> - <paper-input id="changePinOld" class="pin" no-label-float autofocus - label="$i18n{networkSimEnterOldPin}"> - </paper-input> - <paper-input id="changePinNew1" class="pin" no-label-float - label="$i18n{networkSimEnterNewPin}"> - </paper-input> - <paper-input id="changePinNew2" class="pin" no-label-float - label="$i18n{networkSimReEnterNewPin}"> - <iron-a11y-keys keys="enter" on-keys-pressed="sendChangePin_"> - </iron-a11y-keys> - </paper-input> - <div class="dialog-error"> - [[getErrorMsg_(error_, networkProperties)]] - </div> - </div> - <div slot="button-container"> - <paper-button on-tap="sendChangePin_"> - $i18n{networkSimChange} - </paper-button> - </div> - </dialog> - - <!-- Unlock PIN dialog --> - <dialog is="cr-dialog" id="unlockPinDialog" close-text="$i18n{close}" - on-close="onUnlockPinDialogClose_"> - <div slot="title">$i18n{networkSimLockedTitle}</div> - <div slot="body"> - <paper-input id="unlockPin" class="pin" no-label-float autofocus - label="$i18n{networkSimEnterPin}"> - <iron-a11y-keys keys="enter" on-keys-pressed="sendUnlockPin_"> - </iron-a11y-keys> - </paper-input> - <div class="dialog-error"> - [[getErrorMsg_(error_, networkProperties)]] - </div> - </div> - <div slot="button-container"> - <paper-button on-tap="sendUnlockPin_"> - $i18n{networkSimUnlock} - </paper-button> - </div> - </dialog> - - <!-- Unlock PUK dialog --> - <dialog is="cr-dialog" id="unlockPukDialog" close-text="$i18n{close}" - on-close="onUnlockPinDialogClose_"> - <div slot="title">$i18n{networkSimLockedTitle}</div> - <div slot="body"> - <div> - Enter the 8-digit PIN Unblocking Key provided by your carrier - </div> - <paper-input id="unlockPuk" class="puk" no-label-float autofocus - label="$i18n{networkSimEnterPuk}"> - </paper-input> - <paper-input id="unlockPin1" class="pin" no-label-float - label="$i18n{networkSimEnterNewPin}"> - </paper-input> - <paper-input id="unlockPin2" class="pin" no-label-float - label="$i18n{networkSimReEnterNewPin}"> - <iron-a11y-keys keys="enter" on-keys-pressed="sendUnlockPuk_"> - </iron-a11y-keys> - </paper-input> - <div class="dialog-error"> - $i18n{networkSimLockedWarning} - </div> - <div class="dialog-error"> - [[getErrorMsg_(error_, networkProperties)]] - </div> - </div> - <div slot="button-container"> - <paper-button on-tap="sendUnlockPuk_"> - $i18n{networkSimUnlock} - </paper-button> - </div> - </dialog> - </template> - <script src="network_siminfo.js"></script> -</dom-module> diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_siminfo.js b/chromium/chrome/browser/resources/settings/internet_page/network_siminfo.js deleted file mode 100644 index 2ea134f2069..00000000000 --- a/chromium/chrome/browser/resources/settings/internet_page/network_siminfo.js +++ /dev/null @@ -1,402 +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 Polymer element for displaying and modifying cellular sim info. - */ -(function() { - -/** @enum {string} */ -var ErrorType = { - NONE: 'none', - INCORRECT_PIN: 'incorrect-pin', - INCORRECT_PUK: 'incorrect-puk', - MISMATCHED_PIN: 'mismatched-pin', - INVALID_PIN: 'invalid-pin', - INVALID_PUK: 'invalid-puk' -}; - -var PIN_MIN_LENGTH = 4; -var PUK_MIN_LENGTH = 8; - -Polymer({ - is: 'network-siminfo', - - properties: { - /** - * The network properties associated with the element. - * @type {!CrOnc.NetworkProperties|undefined} - */ - networkProperties: { - type: Object, - observer: 'networkPropertiesChanged_', - }, - - /** - * Interface for networkingPrivate calls, passed from internet_page. - * @type {NetworkingPrivate} - */ - networkingPrivate: Object, - - /** - * Reflects networkProperties.Cellular.SIMLockStatus.LockEnabled for the - * toggle button. - * @private - */ - lockEnabled_: { - type: Boolean, - value: false, - }, - - /** - * Set to true when a PUK is required to unlock the SIM. - * @private - */ - pukRequired_: { - type: Boolean, - value: false, - observer: 'pukRequiredChanged_', - }, - - /** - * Set to an ErrorType value after an incorrect PIN or PUK entry. - * @private {ErrorType} - */ - error_: { - type: Object, - value: ErrorType.NONE, - }, - }, - - 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) - return; - var simLockStatus = this.networkProperties.Cellular.SIMLockStatus; - this.pukRequired_ = - !!simLockStatus && simLockStatus.LockType == CrOnc.LockType.PUK; - this.lockEnabled_ = !!simLockStatus && simLockStatus.LockEnabled; - }, - - /** @private */ - pukRequiredChanged_: function() { - if (this.$.unlockPukDialog.open) { - if (this.pukRequired_) - this.$.unlockPuk.focus(); - else - this.$.unlockPukDialog.close(); - return; - } - - if (!this.pukRequired_) - return; - - // If the PUK was activated while attempting to enter or change a pin, - // close the dialog and open the unlock PUK dialog. - var showUnlockPuk = false; - if (this.$.enterPinDialog.open) { - this.$.enterPinDialog.close(); - showUnlockPuk = true; - } - if (this.$.changePinDialog.open) { - this.$.changePinDialog.close(); - showUnlockPuk = true; - } - if (this.$.unlockPinDialog.open) { - this.$.unlockPinDialog.close(); - showUnlockPuk = true; - } - if (!showUnlockPuk) - return; - - this.showUnlockPukDialog_(); - }, - - /** - * Opens the pin dialog when the sim lock enabled state changes. - * @param {!Event} event - * @private - */ - onSimLockEnabledChange_: function(event) { - if (!this.networkProperties || !this.networkProperties.Cellular) - return; - this.sendSimLockEnabled_ = event.target.checked; - this.error_ = ErrorType.NONE; - this.$.enterPin.value = ''; - this.$.enterPinDialog.showModal(); - }, - - /** - * Sends the PIN value from the Enter PIN dialog. - * @param {!Event} event - * @private - */ - sendEnterPin_: function(event) { - event.stopPropagation(); - var guid = (this.networkProperties && this.networkProperties.GUID) || ''; - var pin = this.$.enterPin.value; - if (!this.validatePin_(pin)) { - this.onEnterPinDialogCancel_(); - return; - } - var simState = /** @type {!CrOnc.CellularSimState} */ ({ - currentPin: pin, - requirePin: this.sendSimLockEnabled_, - }); - this.networkingPrivate.setCellularSimState(guid, simState, () => { - if (chrome.runtime.lastError) { - this.error_ = ErrorType.INCORRECT_PIN; - this.$.enterPin.inputElement.select(); - } else { - this.error_ = ErrorType.NONE; - this.$.enterPinDialog.close(); - } - }); - }, - - /** - * Opens the Change PIN dialog. - * @param {!Event} event - * @private - */ - onChangePinTap_: function(event) { - event.stopPropagation(); - if (!this.networkProperties || !this.networkProperties.Cellular) - return; - this.error_ = ErrorType.NONE; - this.$.changePinOld.value = ''; - this.$.changePinNew1.value = ''; - this.$.changePinNew2.value = ''; - this.$.changePinDialog.showModal(); - }, - - /** - * Sends the old and new PIN values from the Change PIN dialog. - * @param {!Event} event - * @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)) - return; - - var simState = /** @type {!CrOnc.CellularSimState} */ ({ - requirePin: true, - currentPin: this.$.changePinOld.value, - newPin: newPin - }); - this.networkingPrivate.setCellularSimState(guid, simState, () => { - if (chrome.runtime.lastError) { - this.error_ = ErrorType.INCORRECT_PIN; - this.$.changePinOld.inputElement.select(); - } else { - this.error_ = ErrorType.NONE; - this.$.changePinDialog.close(); - } - }); - }, - - /** - * Opens the Unlock PIN / PUK dialog. - * @param {!Event} event - * @private - */ - onUnlockPinTap_: function(event) { - event.stopPropagation(); - if (this.pukRequired_) { - this.showUnlockPukDialog_(); - } else { - this.showUnlockPinDialog_(); - } - }, - - /** - * Sends the PIN value from the Unlock PIN dialog. - * @param {!Event} event - * @private - */ - sendUnlockPin_: function(event) { - event.stopPropagation(); - var guid = (this.networkProperties && this.networkProperties.GUID) || ''; - var pin = this.$.unlockPin.value; - if (!this.validatePin_(pin)) - return; - - this.networkingPrivate.unlockCellularSim(guid, pin, '', () => { - if (chrome.runtime.lastError) { - this.error_ = ErrorType.INCORRECT_PIN; - this.$.unlockPin.inputElement.select(); - } else { - this.error_ = ErrorType.NONE; - this.$.unlockPinDialog.close(); - } - }); - }, - - /** @private */ - showUnlockPinDialog_: function() { - this.error_ = ErrorType.NONE; - this.$.unlockPin.value = ''; - this.$.unlockPinDialog.showModal(); - }, - - /** @private */ - showUnlockPukDialog_: function() { - this.error_ = ErrorType.NONE; - this.$.unlockPuk.value = ''; - this.$.unlockPin1.value = ''; - this.$.unlockPin2.value = ''; - this.$.unlockPukDialog.showModal(); - }, - - /** - * Sends the PUK value and new PIN value from the Unblock PUK dialog. - * @param {!Event} event - * @private - */ - sendUnlockPuk_: function(event) { - event.stopPropagation(); - var guid = (this.networkProperties && this.networkProperties.GUID) || ''; - var puk = this.$.unlockPuk.value; - if (!this.validatePuk_(puk)) - return; - var pin = this.$.unlockPin1.value; - if (!this.validatePin_(pin, this.$.unlockPin2.value)) - return; - - this.networkingPrivate.unlockCellularSim(guid, pin, puk, () => { - if (chrome.runtime.lastError) { - this.error_ = ErrorType.INCORRECT_PUK; - this.$.unlockPuk.inputElement.select(); - } else { - this.error_ = ErrorType.NONE; - this.$.unlockPukDialog.close(); - } - }); - }, - - /** - * @return {boolean} - * @private - */ - showSimLocked_: function() { - if (!this.networkProperties || !this.networkProperties.Cellular || - !this.networkProperties.Cellular.SIMPresent) { - return false; - } - return CrOnc.isSimLocked(this.networkProperties); - }, - - /** - * @return {boolean} - * @private - */ - showSimUnlocked_: function() { - if (!this.networkProperties || !this.networkProperties.Cellular || - !this.networkProperties.Cellular.SIMPresent) { - return false; - } - return !CrOnc.isSimLocked(this.networkProperties); - }, - - /** @private */ - getErrorMsg_: function() { - if (this.error_ == ErrorType.NONE) - return ''; - // TODO(stevenjb): Translate - var msg; - if (this.error_ == ErrorType.INCORRECT_PIN) - msg = 'Incorrect PIN.'; - else if (this.error_ == ErrorType.INCORRECT_PUK) - msg = 'Incorrect PUK.'; - else if (this.error_ == ErrorType.MISMATCHED_PIN) - msg = 'PIN values do not match.'; - else if (this.error_ == ErrorType.INVALID_PIN) - msg = 'Invalid PIN.'; - else if (this.error_ == ErrorType.INVALID_PUK) - msg = 'Invalid PUK.'; - else - return 'UNKNOWN ERROR'; - var retriesLeft = - this.get('Cellular.SIMLockStatus.RetriesLeft', this.networkProperties); - if (retriesLeft) { - msg += ' Retries left: ' + retriesLeft.toString(); - } - return msg; - }, - - /** - * Checks whether |pin1| is of the proper length and if opt_pin2 is not - * undefined, whether pin1 and opt_pin2 match. On any failure, sets - * |this.error_| and returns false. - * @param {string} pin1 - * @param {string=} opt_pin2 - * @return {boolean} True if the pins match and are of minimum length. - * @private - */ - validatePin_: function(pin1, opt_pin2) { - if (pin1.length < PIN_MIN_LENGTH) { - this.error_ = ErrorType.INVALID_PIN; - return false; - } - if (opt_pin2 != undefined && pin1 != opt_pin2) { - this.error_ = ErrorType.MISMATCHED_PIN; - return false; - } - return true; - }, - - /** - * Checks whether |puk| is of the proper length. If not, sets |this.error_| - * and returns false. - * @param {string} puk - * @return {boolean} True if the puk is of minimum length. - * @private - */ - validatePuk_: function(puk) { - if (puk.length < PUK_MIN_LENGTH) { - this.error_ = ErrorType.INVALID_PUK; - return false; - } - return true; - }, - - /** @private */ - onEnterPinDialogCancel_: function() { - this.lockEnabled_ = - this.networkProperties.Cellular.SIMLockStatus.LockEnabled; - }, - - /** @private */ - onEnterPinDialogClose_: function() { - cr.ui.focusWithoutInk(assert(this.$$('#simLockButton'))); - }, - - /** @private */ - onChangePinDialogClose_: function() { - cr.ui.focusWithoutInk(assert(this.$$('#changePinButton'))); - }, - - /** @private */ - onUnlockPinDialogClose_: function() { - cr.ui.focusWithoutInk(assert(this.$$('#unlockPinButton'))); - }, -}); -})(); diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_summary.js b/chromium/chrome/browser/resources/settings/internet_page/network_summary.js index a1c6dea2665..a755721b3c4 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/network_summary.js +++ b/chromium/chrome/browser/resources/settings/internet_page/network_summary.js @@ -91,11 +91,10 @@ Polymer({ }, }, - /** - * Listener function for chrome.networkingPrivate.onNetworkListChanged event. - * @private {?function(!Array<string>)} - */ - networkListChangedListener_: null, + listeners: { + 'network-list-changed': 'getNetworkLists_', + 'networks-changed': 'updateActiveNetworks_', + }, /** * Listener function for chrome.networkingPrivate.onDeviceStateListChanged @@ -105,12 +104,6 @@ Polymer({ deviceStateListChangedListener_: null, /** - * Listener function for chrome.networkingPrivate.onNetworksChanged event. - * @private {?function(!Array<string>)} - */ - networksChangedListener_: null, - - /** * Set of GUIDs identifying active networks, one for each type. * @private {?Set<string>} */ @@ -120,41 +113,17 @@ Polymer({ attached: function() { this.getNetworkLists_(); - this.networkListChangedListener_ = this.networkListChangedListener_ || - this.onNetworkListChangedEvent_.bind(this); - this.networkingPrivate.onNetworkListChanged.addListener( - this.networkListChangedListener_); - this.deviceStateListChangedListener_ = this.deviceStateListChangedListener_ || this.onDeviceStateListChangedEvent_.bind(this); this.networkingPrivate.onDeviceStateListChanged.addListener( this.deviceStateListChangedListener_); - - this.networksChangedListener_ = this.networksChangedListener_ || - this.onNetworksChangedEvent_.bind(this); - this.networkingPrivate.onNetworksChanged.addListener( - this.networksChangedListener_); }, /** @override */ detached: function() { - this.networkingPrivate.onNetworkListChanged.removeListener( - assert(this.networkListChangedListener_)); - this.networkingPrivate.onDeviceStateListChanged.removeListener( assert(this.deviceStateListChangedListener_)); - - this.networkingPrivate.onNetworksChanged.removeListener( - assert(this.networksChangedListener_)); - }, - - /** - * networkingPrivate.onNetworkListChanged event callback. - * @private - */ - onNetworkListChangedEvent_: function() { - this.getNetworkLists_(); }, /** @@ -166,13 +135,13 @@ Polymer({ }, /** - * networkingPrivate.onNetworksChanged event callback. - * @param {!Array<string>} networkIds The list of changed network GUIDs. + * @param {{detail: !Array<string>}} event * @private */ - onNetworksChangedEvent_: function(networkIds) { + updateActiveNetworks_: function(event) { if (!this.activeNetworkIds_) return; // Initial list of networks not received yet. + var networkIds = event.detail; networkIds.forEach(function(id) { if (this.activeNetworkIds_.has(id)) { this.networkingPrivate.getState( 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 b1a730a7e82..812bf51fff9 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 @@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/network/network_siminfo.html"> <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_icon.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> @@ -8,7 +9,6 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> <link rel="import" href="../settings_page/settings_subpage.html"> <link rel="import" href="../settings_shared_css.html"> -<link rel="import" href="network_siminfo.html"> <dom-module id="network-summary-item"> <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 9555d961bd8..1171557295f 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 @@ -130,17 +130,24 @@ Polymer({ }, /** - * Show the <network-siminfo> element if this is a disabled and locked - * cellular device. * @param {!CrOnc.DeviceStateProperties|undefined} deviceState * @return {boolean} * @private */ showSimInfo_: function(deviceState) { - if (!deviceState || deviceState.Type != CrOnc.Type.CELLULAR || - this.deviceIsEnabled_(deviceState)) { + if (!deviceState || deviceState.Type != CrOnc.Type.CELLULAR) + return false; + return this.simLockedOrAbsent_(deviceState); + }, + + /** + * @param {!CrOnc.DeviceStateProperties} deviceState + * @return {boolean} + * @private + */ + simLockedOrAbsent_: function(deviceState) { + if (this.deviceIsEnabled_(deviceState)) return false; - } if (deviceState.SIMPresent === false) return true; var simLockType = @@ -184,10 +191,23 @@ Polymer({ * @private */ enableToggleIsVisible_: function(deviceState) { - return !!deviceState && deviceState.Type != CrOnc.Type.ETHERNET && - deviceState.Type != CrOnc.Type.VPN && - (deviceState.Type == CrOnc.Type.TETHER || - deviceState.State != CrOnc.DeviceState.UNINITIALIZED); + if (!deviceState) + return false; + switch (deviceState.Type) { + case CrOnc.Type.ETHERNET: + case CrOnc.Type.VPN: + return false; + case CrOnc.Type.TETHER: + return true; + case CrOnc.Type.WI_FI: + case CrOnc.Type.WI_MAX: + return deviceState.State != CrOnc.DeviceState.UNINITIALIZED; + case CrOnc.Type.CELLULAR: + return deviceState.State != CrOnc.DeviceState.UNINITIALIZED && + !this.simLockedOrAbsent_(deviceState); + } + assertNotReached(); + return false; }, /** diff --git a/chromium/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html b/chromium/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html index 67d7e60dba4..e22b64676bd 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html +++ b/chromium/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html @@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.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/cr_elements/cr_dialog/cr_dialog.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="../icons.html"> diff --git a/chromium/chrome/browser/resources/settings/languages_page/languages.js b/chromium/chrome/browser/resources/settings/languages_page/languages.js index 47a7568bf09..d90b775393c 100644 --- a/chromium/chrome/browser/resources/settings/languages_page/languages.js +++ b/chromium/chrome/browser/resources/settings/languages_page/languages.js @@ -15,6 +15,8 @@ cr.exportPath('settings'); +var MoveType = chrome.languageSettingsPrivate.MoveType; + // Translate server treats some language codes the same. // See also: components/translate/core/common/translate_util.cc. var kLanguageCodeToTranslateCode = { @@ -610,38 +612,22 @@ Polymer({ }, /** - * Moves the language in the list of enabled languages by the given offset. + * Moves the language in the list of enabled languages either up (toward the + * front of the list) or down (toward the back). * @param {string} languageCode - * @param {number} offset Negative offset moves the language toward the front - * of the list. A Positive one moves the language toward the back. + * @param {boolean} upDirection True if we need to move up, false if we + * need to move down */ - moveLanguage: function(languageCode, offset) { + moveLanguage: function(languageCode, upDirection) { if (!CrSettingsPrefs.isInitialized) return; - var languageCodes = - this.getPref(preferredLanguagesPrefName).value.split(','); - - var originalIndex = languageCodes.indexOf(languageCode); - var newIndex = originalIndex; - var direction = Math.sign(offset); - var distance = Math.abs(offset); - - // Step over the distance to find the target index. - while (distance > 0) { - newIndex += direction; - if (newIndex < 0 || newIndex >= languageCodes.length) - return; - - // Skip over non-enabled languages, since they don't appear in the list - // (but we don't want to remove them). - if (this.enabledLanguageSet_.has(languageCodes[newIndex])) - distance--; + if (upDirection) { + this.languageSettingsPrivate_.moveLanguage(languageCode, MoveType.UP); + } else { + this.languageSettingsPrivate_.moveLanguage(languageCode, MoveType.DOWN); } - languageCodes[originalIndex] = languageCodes[newIndex]; - languageCodes[newIndex] = languageCode; - this.setPrefValue(preferredLanguagesPrefName, languageCodes.join(',')); }, /** @@ -652,15 +638,7 @@ Polymer({ if (!CrSettingsPrefs.isInitialized) return; - var languageCodes = - this.getPref(preferredLanguagesPrefName).value.split(','); - var originalIndex = languageCodes.indexOf(languageCode); - assert(originalIndex != -1); - - languageCodes.splice(originalIndex, 1); - languageCodes.unshift(languageCode); - - this.setPrefValue(preferredLanguagesPrefName, languageCodes.join(',')); + this.languageSettingsPrivate_.moveLanguage(languageCode, MoveType.TOP); }, /** diff --git a/chromium/chrome/browser/resources/settings/languages_page/languages_page.html b/chromium/chrome/browser/resources/settings/languages_page/languages_page.html index cfa11d4a722..76bec8e924e 100644 --- a/chromium/chrome/browser/resources/settings/languages_page/languages_page.html +++ b/chromium/chrome/browser/resources/settings/languages_page/languages_page.html @@ -9,10 +9,10 @@ <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.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-toggle-button/paper-toggle-button.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_expand_button/cr_expand_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="add_languages_dialog.html"> <link rel="import" href="languages.html"> @@ -222,11 +222,11 @@ actionable$="[[item.language.supportsSpellcheck]]"> [[item.language.displayName]] </div> - <paper-toggle-button on-change="onSpellCheckChange_" + <cr-toggle on-change="onSpellCheckChange_" disabled="[[!item.language.supportsSpellcheck]]" checked="[[item.spellCheckEnabled]]" aria-label$="[[item.language.displayName]]"> - </paper-toggle-button> + </cr-toggle> </div> </template> <div class="list-item" on-tap="onEditDictionaryTap_" actionable> diff --git a/chromium/chrome/browser/resources/settings/languages_page/languages_page.js b/chromium/chrome/browser/resources/settings/languages_page/languages_page.js index 4c0782d2f48..eef8e646f89 100644 --- a/chromium/chrome/browser/resources/settings/languages_page/languages_page.js +++ b/chromium/chrome/browser/resources/settings/languages_page/languages_page.js @@ -342,7 +342,8 @@ Polymer({ */ onMoveUpTap_: function() { /** @type {!CrActionMenuElement} */ (this.$.menu.get()).close(); - this.languageHelper.moveLanguage(this.detailLanguage_.language.code, -1); + this.languageHelper.moveLanguage( + this.detailLanguage_.language.code, true /* upDirection */); }, /** @@ -351,7 +352,8 @@ Polymer({ */ onMoveDownTap_: function() { /** @type {!CrActionMenuElement} */ (this.$.menu.get()).close(); - this.languageHelper.moveLanguage(this.detailLanguage_.language.code, 1); + this.languageHelper.moveLanguage( + this.detailLanguage_.language.code, false /* upDirection */); }, /** diff --git a/chromium/chrome/browser/resources/settings/languages_page/languages_types.js b/chromium/chrome/browser/resources/settings/languages_page/languages_types.js index af76c645853..8d0833feb41 100644 --- a/chromium/chrome/browser/resources/settings/languages_page/languages_types.js +++ b/chromium/chrome/browser/resources/settings/languages_page/languages_types.js @@ -106,8 +106,8 @@ LanguageHelper.prototype = { /** * Moves the language in the list of enabled languages by the given offset. * @param {string} languageCode - * @param {number} offset Negative offset moves the language toward the front - * of the list. A Positive one moves the language toward the back. + * @param {boolean} upDirection True if we need to move toward the front, + * false if we need to move toward the back. */ moveLanguage: assertNotReached, diff --git a/chromium/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html b/chromium/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html index 0fc0746165b..02b87b2093a 100644 --- a/chromium/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html +++ b/chromium/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html @@ -17,25 +17,29 @@ <dom-module id="settings-startup-urls-page"> <template> <style include="settings-shared action-link iron-flex"> - #outer { + .list-frame { @apply(--settings-list-frame-padding); - max-height: 395px; /** Enough height to show six entries. */ } - #container iron-list > settings-startup-url-entry:not(:first-of-type) { - border-top: var(--settings-separator-line); + .list-frame > div { + border-top: var(--cr-separator-line); + } + + #outer { + @apply(--settings-list-frame-padding); + max-height: 355px; /** Enough height to show six entries. */ } #container settings-startup-url-entry { cursor: default; } </style> - <div id="outer" class="layout vertical flex vertical-list"> + <div id="outer" class="layout vertical flex"> <div id="container" class="scroll-container" scrollable> <iron-list items="[[startupPages_]]" scroll-target="container" - preserve-focus risk-selection> + preserve-focus risk-selection class="cr-separators"> <template> - <settings-startup-url-entry model="[[item]]" + <settings-startup-url-entry model="[[item]]" first$="[[!index]]" tabindex$="[[tabIndex]]" iron-list-tab-index="[[tabIndex]]" last-focused="{{lastFocused_}}" editable="[[shouldAllowUrlsEdit_( @@ -44,6 +48,8 @@ </template> </iron-list> </div> + </div> + <div class="list-frame"> <template is="dom-if" if="[[shouldAllowUrlsEdit_( prefs.session.startup_urls.enforcement)]]" restamp> <div class="list-item" id="addPage"> diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html index f6e65e7231b..4da90c016dc 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html @@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html"> +<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html"> <link rel="import" href="chrome://resources/html/action_link.html"> <link rel="import" href="chrome://resources/html/action_link_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> @@ -57,6 +58,11 @@ text-overflow: ellipsis; white-space: nowrap; } + + cr-policy-indicator { + padding-right: 20px; + width: 20px; + } </style> <settings-toggle-button id="autofillToggle" class="first primary-toggle" @@ -133,51 +139,62 @@ <h2 class="start">$i18n{creditCards}</h2> <paper-button id="addCreditCard" class="secondary-button header-aligned-button" - on-tap="onAddCreditCardTap_"> + on-tap="onAddCreditCardTap_" + hidden$="[[isDisabled_(prefs.autofill.credit_card_enabled)]]"> $i18n{add} </paper-button> </div> <div class="list-frame"> - <div id="creditCardsHeading" class="list-item column-header" - hidden$="[[!hasSome_(creditCards)]]"> - <div class="type-column">$i18n{creditCardType}</div> - <div class="expiration-column">$i18n{creditCardExpiration}</div> - </div> - <div id="creditCardList" class="vertical-list list-with-header"> - <template is="dom-repeat" items="[[creditCards]]"> - <div class="list-item"> - <div class="type-column"> - <span id="creditCardLabel">[[item.metadata.summaryLabel]]</span> - <span class="payments-label" hidden$="[[item.metadata.isLocal]]"> - <span hidden$="[[item.metadata.isCached]]"> - $i18n{googlePayments} - </span> - <span hidden$="[[!item.metadata.isCached]]"> - $i18n{googlePaymentsCached} + <template is="dom-if" + if="[[!isDisabled_(prefs.autofill.credit_card_enabled)]]"> + <div id="creditCardsHeading" class="list-item column-header" + hidden$="[[!hasSome_(creditCards)]]"> + <div class="type-column">$i18n{creditCardType}</div> + <div class="expiration-column">$i18n{creditCardExpiration}</div> + </div> + <div id="creditCardList" class="vertical-list list-with-header"> + <template is="dom-repeat" items="[[creditCards]]"> + <div class="list-item"> + <div class="type-column"> + <span id="creditCardLabel">[[item.metadata.summaryLabel]]</span> + <span class="payments-label" + hidden$="[[item.metadata.isLocal]]"> + <span hidden$="[[item.metadata.isCached]]"> + $i18n{googlePayments} + </span> + <span hidden$="[[!item.metadata.isCached]]"> + $i18n{googlePaymentsCached} + </span> </span> - </span> - </div> - <div class="expiration-column"> - <div id="creditCardExpiration" - class="expiration-date">[[expiration_(item)]]</div> - <template is="dom-if" if="[[showDots_(item.metadata)]]"> - <button is="paper-icon-button-light" id="creditCardMenu" - class="icon-more-vert" title="$i18n{moreActions}" - on-tap="onCreditCardMenuTap_"> - </button> - </template> - <template is="dom-if" if="[[!showDots_(item.metadata)]]"> - <button is="paper-icon-button-light" id="remoteCreditCardLink" - class="icon-external" - on-tap="onRemoteEditCreditCardTap_" actionable></button> - </template> + </div> + <div class="expiration-column"> + <div id="creditCardExpiration" + class="expiration-date">[[expiration_(item)]]</div> + <template is="dom-if" if="[[showDots_(item.metadata)]]"> + <button is="paper-icon-button-light" id="creditCardMenu" + class="icon-more-vert" title="$i18n{moreActions}" + on-tap="onCreditCardMenuTap_"> + </button> + </template> + <template is="dom-if" if="[[!showDots_(item.metadata)]]"> + <button is="paper-icon-button-light" id="remoteCreditCardLink" + class="icon-external" + on-tap="onRemoteEditCreditCardTap_" actionable></button> + </template> + </div> </div> - </div> - </template> - </div> - <div id="noCreditCardsLabel" class="list-item" - hidden$="[[hasSome_(creditCards)]]"> - $i18n{noCreditCardsFound} + </template> + </div> + <div id="noCreditCardsLabel" class="list-item" + hidden$="[[hasSome_(creditCards)]]"> + $i18n{noCreditCardsFound} + </div> + </template> + <div id="CreditCardsDisabledLabel" class="list-item" + hidden$="[[!isDisabled_(prefs.autofill.credit_card_enabled)]]"> + <cr-policy-indicator indicator-type="userPolicy" + icon-aria-label="$i18n{noCreditCardsPolicy}"></cr-policy-indicator> + $i18n{noCreditCardsPolicy} </div> </div> <dialog is="cr-action-menu" id="creditCardSharedMenu"> diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js index ffad7d4c01e..95a22ca7e25 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js @@ -435,6 +435,17 @@ Polymer({ }, /** + * Returns true if the pref has been explicitly disabled. + * @param {Object} pref + * @return {boolean} + * @private + */ + isDisabled_: function(pref) { + return !!pref && (pref.value === false); + }, + + + /** * Listens for the save-address event, and calls the private API. * @param {!Event} event * @private 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 28f9cec9ad3..c637647b887 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 @@ -44,25 +44,25 @@ <div slot="title">$i18n{passwordDetailsTitle}</div> <div slot="body"> <paper-input id="websiteInput" label="$i18n{editPasswordWebsiteLabel}" - value="[[item.loginPair.urls.link]]" readonly always-float-label - on-tap="onReadonlyInputTap_"> + value="[[item.entry.loginPair.urls.link]]" readonly + always-float-label on-tap="onReadonlyInputTap_"> </paper-input> <paper-input id="usernameInput" label="$i18n{editPasswordUsernameLabel}" - value="[[item.loginPair.username]]" readonly always-float-label - on-tap="onReadonlyInputTap_"> + value="[[item.entry.loginPair.username]]" readonly + always-float-label on-tap="onReadonlyInputTap_"> </paper-input> <div id="passwordGroup"> <paper-input id="passwordInput" always-float-label label="$i18n{editPasswordPasswordLabel}" - type="[[getPasswordInputType_(item, password)]]" - value="[[getPassword_(item, password)]]" readonly + type="[[getPasswordInputType_(item.password)]]" + value="[[getPassword_(item.password)]]" readonly on-tap="onReadonlyInputTap_"> </paper-input> <button is="paper-icon-button-light" id="showPasswordButton" - class$="[[getIconClass_(item, password)]]" - hidden$="[[item.federationText]]" + class$="[[getIconClass_(item.password)]]" + hidden$="[[item.entry.federationText]]" on-tap="onShowPasswordButtonTap_" - title="[[showPasswordTitle_(password, + title="[[showPasswordTitle_(item.password, '$i18nPolymer{hidePassword}','$i18nPolymer{showPassword}')]]"> </button> </div> 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 1d11c507149..85dda982896 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 @@ -31,37 +31,37 @@ </style> <div class="list-item" focus-row-container> <div class="website-column no-min-width" - title="[[item.loginPair.urls.link]]"> + title="[[item.entry.loginPair.urls.link]]"> <a id="originUrl" target="_blank" class="no-min-width" - href="[[item.loginPair.urls.link]]" + href="[[item.entry.loginPair.urls.link]]" focus-row-control focus-type="originUrl"> <span class="text-elide"> <!-- This bdo tag is necessary to fix the display of domains starting with numbers. --> - <bdo dir="ltr">[[item.loginPair.urls.shown]]</bdo> + <bdo dir="ltr">[[item.entry.loginPair.urls.shown]]</bdo> </span> </a> </div> <div class="username-column text-elide" - id="username">[[item.loginPair.username]]</div> + id="username">[[item.entry.loginPair.username]]</div> <div class="password-column"> - <template is="dom-if" if="[[!item.federationText]]"> + <template is="dom-if" if="[[!item.entry.federationText]]"> <input id="password" aria-label=$i18n{editPasswordPasswordLabel} - type="[[getPasswordInputType_(item, password)]]" + type="[[getPasswordInputType_(item.password)]]" on-tap="onReadonlyInputTap_" class="password-field" readonly - disabled$="[[!password]]" - value="[[getPassword_(item, password)]]"> + disabled$="[[!item.password]]" + value="[[getPassword_(item.password)]]"> <button is="paper-icon-button-light" id="showPasswordButton" - class$="[[getIconClass_(item, password)]]" + class$="[[getIconClass_(item.password)]]" on-tap="onShowPasswordButtonTap_" - title="[[showPasswordTitle_(password, + title="[[showPasswordTitle_(item.password, '$i18nPolymer{hidePassword}','$i18nPolymer{showPassword}')]]" focus-row-control focus-type="showPassword"> </button> </template> - <template is="dom-if" if="[[item.federationText]]"> + <template is="dom-if" if="[[item.entry.federationText]]"> <span class="password-field text-elide" id="federated"> - [[item.federationText]] + [[item.entry.federationText]] </span> </template> </div> diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.js index 1ade2576b1b..8b2b619fec5 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.js +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.js @@ -27,6 +27,6 @@ Polymer({ */ onPasswordMenuTap_: function() { this.fire( - 'password-menu-tap', {target: this.$.passwordMenu, item: this.item}); + 'password-menu-tap', {target: this.$.passwordMenu, listItem: this}); }, }); diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html new file mode 100644 index 00000000000..6da824f6159 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html @@ -0,0 +1,37 @@ +<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/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="../settings_shared_css.html"> + +<dom-module id="passwords-export-dialog"> + <template> + <style include="settings-shared iron-flex"> + #info-icon { + padding-right: 15px; + } + </style> + <dialog is="cr-dialog" id="dialog" close-text="$i18n{close}"> + <div slot="title">$i18n{exportPasswordsTitle}</div> + <div slot="body"> + <div class="layout horizontal center"> + <iron-icon icon="settings:info-outline" id="info-icon"></iron-icon> + <div>$i18n{exportPasswordsDescription}</div> + </div> + </div> + <div slot="button-container"> + <paper-button class="secondary-button header-aligned-button" + on-tap="onCancelButtonTap_"> + $i18n{cancel} + </paper-button> + <paper-button class="action-button header-aligned-button" + on-tap="onExportTap_" id="exportPasswordsButton"> + $i18n{exportPasswords} + </paper-button> + </div> + </dialog> + </template> + <script src="passwords_export_dialog.js"></script> +</dom-module>
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.js new file mode 100644 index 00000000000..da06563ff1c --- /dev/null +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.js @@ -0,0 +1,52 @@ +// 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 'passwords-export-dialog' is the dialog that allows exporting + * passwords. + */ + +(function() { +'use strict'; + +Polymer({ + is: 'passwords-export-dialog', + + /** + * The interface for callbacks to the browser. + * Defined in passwords_section.js + * @type {PasswordManager} + * @private + */ + passwordManager_: null, + + /** @override */ + attached: function() { + this.$.dialog.showModal(); + + this.passwordManager_ = PasswordManagerImpl.getInstance(); + }, + + /** Closes the dialog. */ + close: function() { + this.$.dialog.close(); + }, + + /** + * Fires an event that should trigger the password export process. + * @private + */ + onExportTap_: function() { + this.passwordManager_.exportPasswords(); + }, + + /** + * Handler for tapping the 'cancel' button. Should just dismiss the dialog. + * @private + */ + onCancelButtonTap_: function() { + this.close(); + }, +}); +})();
\ No newline at end of file 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 fd2f647da10..5c82c295d86 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 @@ -15,6 +15,7 @@ <link rel="import" href="../prefs/prefs.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="password_edit_dialog.html"> +<link rel="import" href="passwords_export_dialog.html"> <link rel="import" href="passwords_shared_css.html"> <link rel="import" href="password_list_item.html"> @@ -84,16 +85,13 @@ </div> <div class="settings-box first"> <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 is="dom-if" + if="[[showImportOrExportPasswords_( + showExportPasswords_, showImportPasswords_)]]"> + <button is="paper-icon-button-light" id="exportImportMenuButton" + class="icon-more-vert" on-tap="onImportExportMenuTap_" + title="$i18n{moreActions}" focus-type="exportImportMenuButton"> + </button> </template> </div> <div class="list-frame"> @@ -109,11 +107,11 @@ </div> <iron-list id="passwordList" preserve-focus items="[[getFilteredPasswords_(savedPasswords, filter)]]" - class="vertical-list list-with-header" + class="cr-separators list-with-header" scroll-target="[[subpageScrollTarget]]" risk-selection> <template> <password-list-item item="[[item]]" tabindex$="[[tabIndex]]" - iron-list-tab-index="[[tabIndex]]" + first$="[[!index]]" iron-list-tab-index="[[tabIndex]]" last-focused="{{lastFocused_}}"> <password-list-item> </template> @@ -129,9 +127,23 @@ <button id="menuRemovePassword" class="dropdown-item" on-tap="onMenuRemovePasswordTap_">$i18n{removePassword}</button> </dialog> + <dialog is="cr-action-menu" id="exportImportMenu"> + <template is="dom-if" if="[[showImportPasswords_]]"> + <button id="menuImportPassword" class="dropdown-item" + on-tap="onImportTap_">$i18n{import}</button> + </template> + <template is="dom-if" if="[[showExportPasswords_]]"> + <button id="menuExportPassword" class="dropdown-item" + on-tap="onExportTap_">$i18n{export}</button> + </template> + </dialog> + <template is="dom-if" if="[[showPasswordsExportDialog_]]" restamp> + <passwords-export-dialog on-close="onPasswordsExportDialogClosed_"> + </passwords-export-dialog> + </template> <template is="dom-if" if="[[showPasswordEditDialog_]]" restamp> <password-edit-dialog on-close="onPasswordEditDialogClosed_" - item="[[activePassword]]"> + item="[[activePassword.item]]"> </password-edit-dialog> </template> <cr-toast id="undoToast" duration="[[toastDuration_]]"> 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 9e5b23059c2..16c02d14e1f 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 @@ -100,6 +100,9 @@ PasswordManager.ExceptionEntry; /** @typedef {chrome.passwordsPrivate.PlaintextPasswordEventParameters} */ PasswordManager.PlaintextPasswordEvent; +/** @typedef {{ entry: !PasswordManager.PasswordUiEntry, password: string }} */ +PasswordManager.UiEntryWithPassword; + /** * Implementation that accesses the private API. * @implements {PasswordManager} @@ -234,7 +237,7 @@ Polymer({ /** * The model for any password related action menus or dialogs. - * @private {?chrome.passwordsPrivate.PasswordUiEntry} + * @private {?PasswordListItemElement} */ activePassword: Object, @@ -246,11 +249,20 @@ Polymer({ }, /** @private */ - showImportExportPasswords_: { + showExportPasswords_: { + type: Boolean, + value: function() { + return loadTimeData.valueExists('showExportPasswords') && + loadTimeData.getBoolean('showExportPasswords'); + } + }, + + /** @private */ + showImportPasswords_: { type: Boolean, value: function() { - return loadTimeData.valueExists('showImportExportPasswords') && - loadTimeData.getBoolean('showImportExportPasswords'); + return loadTimeData.valueExists('showImportPasswords') && + loadTimeData.getBoolean('showImportPasswords'); } }, @@ -270,6 +282,7 @@ Polymer({ listeners: { 'show-password': 'showPassword_', 'password-menu-tap': 'onPasswordMenuTap_', + 'export-passwords': 'onExportPasswords_', }, keyBindings: { @@ -310,7 +323,12 @@ Polymer({ attached: function() { // Create listener functions. var setSavedPasswordsListener = list => { - this.savedPasswords = list; + this.savedPasswords = list.map(entry => { + return { + entry: entry, + password: '', + }; + }); }; var setPasswordExceptionsListener = list => { @@ -346,6 +364,9 @@ Polymer({ this.passwordManager_.removeExceptionListChangedListener( /** @type {function(!Array<PasswordManager.ExceptionEntry>):void} */ ( this.setPasswordExceptionsListener_)); + + if (this.$.undoToast.open) + this.$.undoToast.hide(); }, /** @@ -364,12 +385,16 @@ Polymer({ this.showPasswordEditDialog_ = false; cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_)); this.activeDialogAnchor_ = null; + + // Trigger a re-evaluation of the activePassword as the visibility state of + // the password might have changed. + this.activePassword.notifyPath('item.password'); }, /** - * @param {!Array<!chrome.passwordsPrivate.PasswordUiEntry>} savedPasswords + * @param {!Array<!PasswordManager.UiEntryWithPassword>} savedPasswords * @param {string} filter - * @return {!Array<!chrome.passwordsPrivate.PasswordUiEntry>} + * @return {!Array<!PasswordManager.UiEntryWithPassword>} * @private */ getFilteredPasswords_: function(savedPasswords, filter) { @@ -377,7 +402,7 @@ Polymer({ return savedPasswords; return savedPasswords.filter(p => { - return [p.loginPair.urls.shown, p.loginPair.username].some( + return [p.entry.loginPair.urls.shown, p.entry.loginPair.username].some( term => term.toLowerCase().includes(filter.toLowerCase())); }); }, @@ -398,7 +423,8 @@ Polymer({ * @private */ onMenuRemovePasswordTap_: function() { - this.passwordManager_.removeSavedPassword(this.activePassword.index); + this.passwordManager_.removeSavedPassword( + this.activePassword.item.entry.index); this.fire('iron-announce', {text: this.$.undoLabel.textContent}); this.$.undoToast.show(); /** @type {CrActionMenuElement} */ (this.$.menu).close(); @@ -435,8 +461,20 @@ Polymer({ var target = /** @type {!HTMLElement} */ (event.detail.target); this.activePassword = - /** @type {!chrome.passwordsPrivate.PasswordUiEntry} */ ( - event.detail.item); + /** @type {!PasswordListItemElement} */ (event.detail.listItem); + menu.showAt(target); + this.activeDialogAnchor_ = target; + }, + + /** + * Opens the export/import action menu. + * @private + */ + onImportExportMenuTap_: function() { + var menu = /** @type {!CrActionMenuElement} */ (this.$.exportImportMenu); + var target = + /** @type {!HTMLElement} */ (this.$$('#exportImportMenuButton')); + menu.showAt(target); this.activeDialogAnchor_ = target; }, @@ -454,11 +492,17 @@ Polymer({ }, /** - * Fires an event that should trigger the password export process. + * Opens the export passwords dialog. * @private */ onExportTap_: function() { - this.passwordManager_.exportPasswords(); + this.showPasswordsExportDialog_ = true; + this.$.exportImportMenu.close(); + }, + + /** @private */ + onPasswordsExportDialogClosed_: function() { + this.showPasswordsExportDialog_ = false; }, /** @@ -478,8 +522,8 @@ Polymer({ */ showPassword_: function(event) { this.passwordManager_.getPlaintextPassword( - /** @type {!number} */ (event.detail.item.index), item => { - event.detail.password = item.plaintextPassword; + /** @type {!number} */ (event.detail.item.entry.index), item => { + event.detail.set('item.password', item.plaintextPassword); }); }, @@ -490,6 +534,17 @@ Polymer({ */ getOnOffLabel_: function(toggleValue) { return toggleValue ? this.i18n('toggleOn') : this.i18n('toggleOff'); + }, + + /** + * @private + * @param {boolean} showExportPasswords + * @param {boolean} showImportPasswords + * @return {boolean} + */ + showImportOrExportPasswords_: function( + showExportPasswords, showImportPasswords) { + return showExportPasswords || showImportPasswords; } }); })(); diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/show_password_behavior.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/show_password_behavior.js index 8c4a54bf5b0..52c7fcf8338 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/show_password_behavior.js +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/show_password_behavior.js @@ -13,19 +13,9 @@ var ShowPasswordBehavior = { properties: { /** * The password that is being displayed. - * @type {!chrome.passwordsPrivate.PasswordUiEntry} + * @type {!ShowPasswordBehavior.UiEntryWithPassword} */ item: Object, - - /** - * Holds the plaintext password when requested. - * Initializing it to the empty string is necessary to indicate that the - * password hasn't been fetched yet. - */ - password: { - type: String, - value: '', - }, }, /** @@ -34,7 +24,8 @@ var ShowPasswordBehavior = { * @private */ getPasswordInputType_: function() { - return this.password || this.item.federationText ? 'text' : 'password'; + return this.item.password || this.item.entry.federationText ? 'text' : + 'password'; }, /** @@ -54,7 +45,7 @@ var ShowPasswordBehavior = { * @private */ getIconClass_: function() { - return this.password ? 'icon-visibility-off' : 'icon-visibility'; + return this.item.password ? 'icon-visibility-off' : 'icon-visibility'; }, /** @@ -66,9 +57,8 @@ var ShowPasswordBehavior = { getPassword_: function() { if (!this.item) return ''; - - return this.item.federationText || this.password || - ' '.repeat(this.item.numCharactersInPassword); + return this.item.entry.federationText || this.item.password || + ' '.repeat(this.item.entry.numCharactersInPassword); }, /** @@ -77,9 +67,16 @@ var ShowPasswordBehavior = { * @private */ onShowPasswordButtonTap_: function() { - if (this.password) - this.password = ''; + if (this.item.password) + this.set('item.password', ''); else this.fire('show-password', this); // Request the password. }, }; + +/** @typedef {{ + * entry: !chrome.passwordsPrivate.PasswordUiEntry, + * password: string + * }} + */ +ShowPasswordBehavior.UiEntryWithPassword; 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 82cbc58d44a..87ec0b1917d 100644 --- a/chromium/chrome/browser/resources/settings/people_page/change_picture.html +++ b/chromium/chrome/browser/resources/settings/people_page/change_picture.html @@ -84,7 +84,10 @@ discard-image-label="$i18n{discardPhoto}" preview-alt-text="$i18n{previewAltText}" take-photo-label="$i18n{takePhoto}" - switch-mode-label="$i18n{switchMode}"> + capture-video-label="$i18n{captureVideo}" + switch-mode-to-camera-label="$i18n{switchModeToCamera}" + switch-mode-to-video-label="$i18n{switchModeToVideo}" + camera-video-mode-enabled="[[cameraVideoModeEnabled_]]"> </cr-picture-pane> <div id="authorCredit" hidden="[[!isAuthorCreditShown_(selectedItem_)]]"> @@ -103,8 +106,8 @@ choose-file-label="$i18n{chooseFile}" old-image-label="$i18n{oldPhoto}" profile-image-label="$i18n{profilePhoto}" - take-photo-label="$i18n{takePhoto}" - switch-mode-label="$i18n{switchMode}"> + take-photo-label="$i18n{takePhoto}"> + capture-video-label="$i18n{captureVideo}"> </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 702225517b3..0703e896395 100644 --- a/chromium/chrome/browser/resources/settings/people_page/change_picture.js +++ b/chromium/chrome/browser/resources/settings/people_page/change_picture.js @@ -53,6 +53,15 @@ Polymer({ * @private */ firstDefaultImageIndex_: Number, + + /** + * True when camera video mode is enabled. + * @private {boolean} + */ + cameraVideoModeEnabled_: { + type: Boolean, + value: false, + }, }, listeners: { @@ -173,7 +182,7 @@ Polymer({ case CrPicture.SelectionTypes.OLD: var imageIndex = image.dataset.imageIndex; if (imageIndex !== undefined && imageIndex >= 0 && image.src) - this.browserProxy_.selectDefaultImage(image.src); + this.browserProxy_.selectDefaultImage(image.dataset.url); else this.browserProxy_.selectOldImage(); break; diff --git a/chromium/chrome/browser/resources/settings/people_page/manage_profile.html b/chromium/chrome/browser/resources/settings/people_page/manage_profile.html index 656400bd7c9..fbc49f4ef9d 100644 --- a/chromium/chrome/browser/resources/settings/people_page/manage_profile.html +++ b/chromium/chrome/browser/resources/settings/people_page/manage_profile.html @@ -1,10 +1,10 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.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-styles/shadow.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="manage_profile_browser_proxy.html"> <link rel="import" href="../route.html"> @@ -27,11 +27,11 @@ <template is="dom-if" if="[[isProfileShortcutSettingVisible_]]"> <div class="settings-box first"> <div id="showShortcutLabel" class="start">$i18n{showShortcutLabel}</div> - <paper-toggle-button id="hasShortcutToggle" + <cr-toggle id="hasShortcutToggle" checked="{{hasProfileShortcut_}}" on-change="onHasProfileShortcutChange_" aria-labelledby="showShortcutLabel"> - </paper-toggle-button> + </cr-toggle> </div> </template> <cr-profile-avatar-selector id="selector" avatars="[[availableIcons]]" diff --git a/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.js b/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.js index 2d3035ada0d..5115c11f17b 100644 --- a/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.js +++ b/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.js @@ -136,6 +136,12 @@ Polymer({ // The password might have been cleared during the duration of the // getActiveModes call. this.passwordInvalid_ = !valid && !!this.password_; + // Return focus to the password input if it lost focus while being checked + // (user pressed confirm button). + if (this.passwordInvalid_ && + this.shadowRoot.activeElement != this.$.passwordInput) { + this.$.passwordInput.focus(); + } if (valid) { // Create the |this.setModes| closure and automatically clear it after 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 cec6369f63b..4ab0dfcd81f 100644 --- a/chromium/chrome/browser/resources/settings/people_page/people_page.html +++ b/chromium/chrome/browser/resources/settings/people_page/people_page.html @@ -274,7 +274,11 @@ <settings-subpage associated-control="[[$$('#manage-other-people-subpage-trigger')]]" page-title="$i18n{manageOtherPeople}"> - <settings-users-page prefs="{{prefs}}"></settings-users-page> + <settings-users-page + prefs="{{prefs}}" + profile-manages-supervised-users= + "[[profileManagesSupervisedUsers_]]"> + </settings-users-page> </settings-subpage> </template> <template is="dom-if" route-path="/changePicture"> diff --git a/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.html b/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.html index bdee32b9efe..d7a74e2a102 100644 --- a/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.html +++ b/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.html @@ -1,10 +1,10 @@ +<link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/html/polymer.html"> <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/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="../i18n_setup.html"> -<link rel="import" href="../icons.html"> <link rel="import" href="lock_screen_constants.html"> <link rel="import" href="pin_keyboard.html"> <link rel="import" href="../settings_shared_css.html"> @@ -55,7 +55,7 @@ <!-- Warning/error; only shown if title is hidden. --> <div id="problemDiv" class$="[[problemClass_]]" hidden="[[!problemMessage_]]" problem> - <iron-icon icon="settings:error-outline"></iron-icon> + <iron-icon icon="cr:error-outline"></iron-icon> <span id="problemMessage">[[problemMessage_]]</span> </div> </pin-keyboard> diff --git a/chromium/chrome/browser/resources/settings/people_page/sync_page.html b/chromium/chrome/browser/resources/settings/people_page/sync_page.html index 9d6fba87a42..07ae46a0bac 100644 --- a/chromium/chrome/browser/resources/settings/people_page/sync_page.html +++ b/chromium/chrome/browser/resources/settings/people_page/sync_page.html @@ -3,9 +3,9 @@ <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/cr_elements/cr_toggle/cr_toggle.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="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-radio-button/paper-radio-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-radio-group/paper-radio-group.html"> @@ -103,23 +103,23 @@ <div id="syncEverythingCheckboxLabel" class="start"> $i18n{syncEverythingCheckboxLabel} </div> - <paper-toggle-button id="syncAllDataTypesControl" + <cr-toggle id="syncAllDataTypesControl" checked="{{syncPrefs.syncAllDataTypes}}" on-change="onSyncAllDataTypesChanged_" aria-labelledby="syncEverythingCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> <div class="list-frame" id="sync-data-types"> <div class="layout horizontal list-item" hidden="[[!syncPrefs.appsRegistered]]"> <div id="appCheckboxLabel" class="flex">$i18n{appCheckboxLabel}</div> - <paper-toggle-button checked="{{syncPrefs.appsSynced}}" + <cr-toggle checked="{{syncPrefs.appsSynced}}" on-change="onSingleSyncDataTypeChanged_" disabled="[[shouldSyncCheckboxBeDisabled_( syncPrefs.syncAllDataTypes, syncPrefs.appsEnforced)]]" aria-labelledby="appCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> <div class="layout horizontal list-item" @@ -129,12 +129,12 @@ </div> <!-- Autofill has a special on-change handler to deal with Payments integriation. --> - <paper-toggle-button checked="{{syncPrefs.autofillSynced}}" + <cr-toggle checked="{{syncPrefs.autofillSynced}}" on-change="onAutofillDataTypeChanged_" disabled="[[shouldSyncCheckboxBeDisabled_( syncPrefs.syncAllDataTypes, syncPrefs.autofillEnforced)]]" aria-labelledby="autofillCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> <div class="layout horizontal list-item" @@ -142,12 +142,12 @@ <div id="bookmarksCheckboxLabel" class="flex"> $i18n{bookmarksCheckboxLabel} </div> - <paper-toggle-button checked="{{syncPrefs.bookmarksSynced}}" + <cr-toggle checked="{{syncPrefs.bookmarksSynced}}" on-change="onSingleSyncDataTypeChanged_" disabled="[[shouldSyncCheckboxBeDisabled_( syncPrefs.syncAllDataTypes, syncPrefs.bookmarksEnforced)]]" aria-labelledby="bookmarksCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> <div class="layout horizontal list-item" @@ -155,12 +155,12 @@ <div id="extensionsCheckboxLabel" class="flex"> $i18n{extensionsCheckboxLabel} </div> - <paper-toggle-button checked="{{syncPrefs.extensionsSynced}}" + <cr-toggle checked="{{syncPrefs.extensionsSynced}}" on-change="onSingleSyncDataTypeChanged_" disabled="[[shouldSyncCheckboxBeDisabled_( syncPrefs.syncAllDataTypes, syncPrefs.extensionsEnforced)]]" aria-labelledby="extensionsCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> <div class="layout horizontal list-item" @@ -168,12 +168,12 @@ <div id="historyCheckboxLabel" class="flex"> $i18n{historyCheckboxLabel} </div> - <paper-toggle-button checked="{{syncPrefs.typedUrlsSynced}}" + <cr-toggle checked="{{syncPrefs.typedUrlsSynced}}" on-change="onSingleSyncDataTypeChanged_" disabled="[[shouldSyncCheckboxBeDisabled_( syncPrefs.syncAllDataTypes, syncPrefs.typedUrlsEnforced)]]" aria-labelledby="historyCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> <div class="layout horizontal list-item" @@ -181,12 +181,12 @@ <div id="passwordsCheckboxLabel" class="flex"> $i18n{passwordsCheckboxLabel} </div> - <paper-toggle-button checked="{{syncPrefs.passwordsSynced}}" + <cr-toggle checked="{{syncPrefs.passwordsSynced}}" on-change="onSingleSyncDataTypeChanged_" disabled="[[shouldSyncCheckboxBeDisabled_( syncPrefs.syncAllDataTypes, syncPrefs.passwordsEnforced)]]" aria-labelledby="passwordsCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> <div class="layout horizontal list-item" @@ -194,12 +194,12 @@ <div id="settingsCheckboxLabel" class="flex"> $i18n{settingsCheckboxLabel} </div> - <paper-toggle-button checked="{{syncPrefs.preferencesSynced}}" + <cr-toggle checked="{{syncPrefs.preferencesSynced}}" on-change="onSingleSyncDataTypeChanged_" disabled="[[shouldSyncCheckboxBeDisabled_( syncPrefs.syncAllDataTypes, syncPrefs.preferencesEnforced)]]" aria-labelledby="settingsCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> <div class="layout horizontal list-item" @@ -207,12 +207,12 @@ <div id="themesAndWallpapersCheckboxLabel" class="flex"> $i18n{themesAndWallpapersCheckboxLabel} </div> - <paper-toggle-button checked="{{syncPrefs.themesSynced}}" + <cr-toggle checked="{{syncPrefs.themesSynced}}" on-change="onSingleSyncDataTypeChanged_" disabled="[[shouldSyncCheckboxBeDisabled_( syncPrefs.syncAllDataTypes, syncPrefs.themesEnforced)]]" aria-labelledby="themesAndWallpapersCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> <div class="layout horizontal list-item" @@ -220,12 +220,12 @@ <div id="openTabsCheckboxLabel" class="flex"> $i18n{openTabsCheckboxLabel} </div> - <paper-toggle-button checked="{{syncPrefs.tabsSynced}}" + <cr-toggle checked="{{syncPrefs.tabsSynced}}" on-change="onSingleSyncDataTypeChanged_" disabled="[[shouldSyncCheckboxBeDisabled_( syncPrefs.syncAllDataTypes, syncPrefs.tabsEnforced)]]" aria-labelledby="openTabsCheckboxLabel"> - </paper-toggle-button> + </cr-toggle> </div> <div class="layout horizontal list-item" @@ -239,13 +239,13 @@ $i18n{learnMore} </a> </div> - <paper-toggle-button + <cr-toggle checked="{{syncPrefs.paymentsIntegrationEnabled}}" on-change="onSingleSyncDataTypeChanged_" disabled="[[shouldPaymentsCheckboxBeDisabled_( syncPrefs.syncAllDataTypes, syncPrefs.autofillSynced)]]" aria-label="$i18n{enablePaymentsIntegrationCheckboxLabel}"> - </paper-toggle-button> + </cr-toggle> </div> </div> diff --git a/chromium/chrome/browser/resources/settings/people_page/users_page.html b/chromium/chrome/browser/resources/settings/people_page/users_page.html index a2fea7f100f..ac720c8628e 100644 --- a/chromium/chrome/browser/resources/settings/people_page/users_page.html +++ b/chromium/chrome/browser/resources/settings/people_page/users_page.html @@ -39,11 +39,13 @@ label="$i18n{guestBrowsingLabel}" disabled="[[isEditingDisabled_(isOwner_, isWhitelistManaged_)]]"> </settings-toggle-button> - <settings-toggle-button class="continuation" - pref="{{prefs.cros.accounts.supervisedUsersEnabled}}" - label="$i18n{supervisedUsersLabel}" - disabled="[[isEditingDisabled_(isOwner_, isWhitelistManaged_)]]"> - </settings-toggle-button> + <template is="dom-if" if="[[profileManagesSupervisedUsers]]"> + <settings-toggle-button class="continuation" + pref="{{prefs.cros.accounts.supervisedUsersEnabled}}" + label="$i18n{supervisedUsersLabel}" + disabled="[[isEditingDisabled_(isOwner_, isWhitelistManaged_)]]"> + </settings-toggle-button> + </template> <settings-toggle-button class="continuation" pref="{{prefs.cros.accounts.showUserNamesOnSignIn}}" label="$i18n{showOnSigninLabel}" diff --git a/chromium/chrome/browser/resources/settings/people_page/users_page.js b/chromium/chrome/browser/resources/settings/people_page/users_page.js index 2d14833a0dc..762fb6763f8 100644 --- a/chromium/chrome/browser/resources/settings/people_page/users_page.js +++ b/chromium/chrome/browser/resources/settings/people_page/users_page.js @@ -19,6 +19,15 @@ Polymer({ notify: true, }, + /** + * True if the current profile manages supervised users. + * Set in people-page. + */ + profileManagesSupervisedUsers: { + type: Boolean, + value: false, + }, + /** @private */ isOwner_: { type: Boolean, diff --git a/chromium/chrome/browser/resources/settings/printing_page/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/printing_page/compiled_resources2.gyp index 6a3b5f8fddf..cc0e0003a42 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/printing_page/compiled_resources2.gyp @@ -72,6 +72,13 @@ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, { + 'target_name': 'printing_browser_proxy', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'printing_page', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', @@ -79,6 +86,7 @@ '../compiled_resources2.gyp:route', '../settings_page/compiled_resources2.gyp:settings_animated_pages', 'cups_printers_browser_proxy', + 'printing_browser_proxy', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html index 5ca6f7e45de..5f697cc8706 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html @@ -61,7 +61,7 @@ $i18n{cancelButtonText} </paper-button> <paper-button class="action-button" id="addPrinterButton" - disabled="[[!selectedPrinter]]" + disabled="[[!canAddPrinter_(selectedPrinter)]]" on-tap="switchToConfiguringDialog_"> $i18n{addPrinterButtonText} </paper-button> 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 6ebd338e294..ccd0c66fcdf 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 @@ -143,6 +143,15 @@ Polymer({ this.close(); this.fire('open-configuring-printer-dialog'); }, + + /** + * @param {?CupsPrinterInfo} selectedPrinter + * @return {boolean} Whether the add printer button is enabled. + * @private + */ + canAddPrinter_: function(selectedPrinter) { + return !!selectedPrinter && !!selectedPrinter.printerName; + }, }); Polymer({ 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 0957580006d..727755f4a89 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_printers.js +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_printers.js @@ -115,7 +115,7 @@ Polymer({ break; case PrinterSetupResult.PRINTER_UNREACHABLE: messageText.textContent = - loadTimeData.getString('printerAddedPrinterUnreachableMessage'); + loadTimeData.getString('printerAddedUnreachableMessage'); break; case PrinterSetupResult.DBUS_ERROR: // Simply display a generic error message as this error should only 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 6cc0b682015..a1804172813 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 @@ -4,7 +4,7 @@ /** * @fileoverview A helper object used from the "CUPS printing" section to - * interact with the browser. + * interact with the browser. Used only on Chrome OS. */ /** diff --git a/chromium/chrome/browser/resources/settings/printing_page/printing_browser_proxy.html b/chromium/chrome/browser/resources/settings/printing_page/printing_browser_proxy.html new file mode 100644 index 00000000000..8e6d297ef56 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/printing_page/printing_browser_proxy.html @@ -0,0 +1,2 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="printing_browser_proxy.js"></script> diff --git a/chromium/chrome/browser/resources/settings/printing_page/printing_browser_proxy.js b/chromium/chrome/browser/resources/settings/printing_page/printing_browser_proxy.js new file mode 100644 index 00000000000..83b8536e836 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/printing_page/printing_browser_proxy.js @@ -0,0 +1,35 @@ +// 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 Chrome printing section to + * interact with the browser. Used on operating system that is not Chrome OS. + */ + +cr.define('settings', function() { + /** @interface */ + class PrintingBrowserProxy { + /** + * Open the native print system dialog. + */ + openSystemPrintDialog() {} + } + + /** + * @implements {settings.PrintingBrowserProxy} + */ + class PrintingBrowserProxyImpl { + /** @override */ + openSystemPrintDialog() { + chrome.send('openSystemPrintDialog'); + } + } + + cr.addSingletonGetter(PrintingBrowserProxyImpl); + + return { + PrintingBrowserProxy: PrintingBrowserProxy, + PrintingBrowserProxyImpl: PrintingBrowserProxyImpl, + }; +}); diff --git a/chromium/chrome/browser/resources/settings/printing_page/printing_page.html b/chromium/chrome/browser/resources/settings/printing_page/printing_page.html index 77eddcbbdfc..90e18fbb2e5 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/printing_page.html +++ b/chromium/chrome/browser/resources/settings/printing_page/printing_page.html @@ -10,6 +10,9 @@ <if expr="chromeos"> <link rel="import" href="cups_printers.html"> </if> +<if expr="not chromeos"> +<link rel="import" href="printing_browser_proxy.html"> +</if> <dom-module id="settings-printing-page"> <template> @@ -25,6 +28,14 @@ aria-label="$i18n{cupsPrintersTitle}"></button> </div> </if> +<if expr="not chromeos"> + <div class="settings-box first" + on-tap="onTapLocalPrinters_" actionable> + <div class="start">$i18n{localPrintersTitle}</div> + <button class="subpage-arrow" is="paper-icon-button-light" + aria-label="$i18n{localPrintersTitle}"></button> + </div> +</if> <div id="cloudPrinters" class="settings-box" on-tap="onTapCloudPrinters_" actionable> <div class="start">$i18n{cloudPrintersTitle}</div> diff --git a/chromium/chrome/browser/resources/settings/printing_page/printing_page.js b/chromium/chrome/browser/resources/settings/printing_page/printing_page.js index 8c8e14b2934..79a7be19b24 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/printing_page.js +++ b/chromium/chrome/browser/resources/settings/printing_page/printing_page.js @@ -45,6 +45,12 @@ Polymer({ }, // </if> + // <if expr="not chromeos"> + onTapLocalPrinters_: function() { + settings.PrintingBrowserProxyImpl.getInstance().openSystemPrintDialog(); + }, + // </if> + /** @private */ onTapCloudPrinters_: function() { settings.navigateTo(settings.routes.CLOUD_PRINTERS); 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 52fff4a8457..1ba4c86e60b 100644 --- a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html +++ b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html @@ -349,8 +349,8 @@ <template is="dom-if" route-path="/content/sound" no-search> <settings-subpage page-title="$i18n{siteSettingsSound}"> <category-default-setting - toggle-off-label="$i18n{siteSettingsBlocked}" - toggle-on-label="$i18n{siteSettingsAllowedRecommended}" + toggle-off-label="$i18n{siteSettingsSoundBlock}" + toggle-on-label="$i18n{siteSettingsSoundAllowRecommended}" category="{{ContentSettingsTypes.SOUND}}"> </category-default-setting> <category-setting-exceptions @@ -358,8 +358,8 @@ block-header="$i18n{siteSettingsBlockSound}"> </category-setting-exceptions> </settings-subpage> - </template> - </template> + </template> + </template> <template is="dom-if" route-path="/content/microphone" no-search> <settings-subpage page-title="$i18n{siteSettingsCategoryMicrophone}"> <media-picker label="$i18n{siteSettingsMicrophoneLabel}" type="mic"> @@ -516,6 +516,22 @@ </if> </settings-subpage> </template> + <template is="dom-if" if="[[enableClipboardContentSetting_]]" + no-search> + <template is="dom-if" route-path="/content/clipboard" no-search> + <settings-subpage page-title="$i18n{siteSettingsClipboard}"> + <category-default-setting + toggle-off-label="$i18n{siteSettingsClipboardBlock}" + toggle-on-label="$i18n{siteSettingsClipboardAskRecommended}" + category="{{ContentSettingsTypes.CLIPBOARD}}"> + </category-default-setting> + <category-setting-exceptions + category="{{ContentSettingsTypes.CLIPBOARD}}" + block-header="$i18n{siteSettingsBlock}"> + </category-setting-exceptions> + </settings-subpage> + </template> + </template> </settings-animated-pages> </template> <script src="privacy_page.js"></script> diff --git a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js index cb15fd06ff5..46afb457824 100644 --- a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js +++ b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js @@ -115,6 +115,14 @@ Polymer({ } }, + /** @private */ + enableClipboardContentSetting_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('enableClipboardContentSetting'); + } + }, + /** @private {!Map<string, string>} */ focusConfig_: { type: Object, diff --git a/chromium/chrome/browser/resources/settings/reset_page/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/reset_page/compiled_resources2.gyp index c1e99cfafce..39f600e4c60 100644 --- a/chromium/chrome/browser/resources/settings/reset_page/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/reset_page/compiled_resources2.gyp @@ -11,6 +11,25 @@ '<(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:focus_without_ink', + 'reset_profile_dialog', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'reset_browser_proxy', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(EXTERNS_GYP):chrome_send', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'reset_profile_dialog', + 'dependencies': [ + '../compiled_resources2.gyp:route', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', + 'reset_browser_proxy', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, diff --git a/chromium/chrome/browser/resources/settings/reset_page/reset_browser_proxy.js b/chromium/chrome/browser/resources/settings/reset_page/reset_browser_proxy.js index f43c9a08a38..8b9f2184c51 100644 --- a/chromium/chrome/browser/resources/settings/reset_page/reset_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/reset_page/reset_browser_proxy.js @@ -114,6 +114,7 @@ cr.define('settings', function() { cr.addSingletonGetter(ResetBrowserProxyImpl); return { + ResetBrowserProxy: ResetBrowserProxy, ResetBrowserProxyImpl: ResetBrowserProxyImpl, }; }); diff --git a/chromium/chrome/browser/resources/settings/reset_page/reset_page.html b/chromium/chrome/browser/resources/settings/reset_page/reset_page.html index fc55ba1f8f4..44214d07820 100644 --- a/chromium/chrome/browser/resources/settings/reset_page/reset_page.html +++ b/chromium/chrome/browser/resources/settings/reset_page/reset_page.html @@ -3,53 +3,95 @@ <link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.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/polymer/v1_0/neon-animation/neon-animatable.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="reset_profile_dialog.html"> <link rel="import" href="../route.html"> +<link rel="import" href="../settings_page/settings_animated_pages.html"> <link rel="import" href="../settings_shared_css.html"> <if expr="chromeos"> <link rel="import" href="powerwash_dialog.html"> </if> +<if expr="_google_chrome and is_win"> +<link rel="import" href="../chrome_cleanup_page/chrome_cleanup_page.html"> +</if> + <dom-module id="settings-reset-page"> <template> <style include="settings-shared"></style> - <div class="settings-box first two-line" id="resetProfile" - on-tap="onShowResetProfileDialog_" actionable> - <div class="start"> - $i18n{resetPageTitle} - <div class="secondary" id="resetProfileSecondary"> - $i18n{resetPageDescription} + <settings-animated-pages id="reset-pages" section="reset"> + <neon-animatable route-path="default"> + <div class="settings-box first two-line" id="resetProfile" + on-tap="onShowResetProfileDialog_" actionable> + <div class="start"> + $i18n{resetPageTitle} + <div class="secondary" id="resetProfileSecondary"> + $i18n{resetPageDescription} + </div> + </div> + <button id="resetProfileArrow" is="paper-icon-button-light" + class="subpage-arrow" aria-label="$i18n{resetPageTitle}" + aria-describedby="resetProfileSecondary"></button> </div> - </div> - <button id="resetProfileArrow" is="paper-icon-button-light" - class="subpage-arrow" aria-label="$i18n{resetPageTitle}" - aria-describedby="resetProfileSecondary"></button> - </div> - <template is="dom-if" if="[[showResetProfileDialog_]]" restamp> - <settings-reset-profile-dialog on-close="onResetProfileDialogClose_"> - </settings-reset-profile-dialog> - </template> + <!-- Keep a single instance of reset-profile-dialog on purpose, to + preserve state across show/hide operations. --> + <template is="cr-lazy-render" id="resetProfileDialog"> + <settings-reset-profile-dialog on-close="onResetProfileDialogClose_"> + </settings-reset-profile-dialog> + </template> <if expr="chromeos"> - <div class="settings-box two-line" id="powerwash" actionable - on-tap="onShowPowerwashDialog_" hidden="[[!allowPowerwash_]]"> - <div class="start"> - $i18n{powerwashTitle} - <div class="secondary" id="powerwashSecondary"> - $i18n{powerwashDescription} + <div class="settings-box two-line" id="powerwash" actionable + on-tap="onShowPowerwashDialog_" hidden="[[!allowPowerwash_]]"> + <div class="start"> + $i18n{powerwashTitle} + <div class="secondary" id="powerwashSecondary"> + $i18n{powerwashDescription} + </div> + </div> + <button id="powerwashArrow" is="paper-icon-button-light" + class="subpage-arrow" aria-label="$i18n{powerwashTitle}" + aria-describedby="powerwashSecondary"></button> </div> - </div> - <button id="powerwashArrow" is="paper-icon-button-light" - class="subpage-arrow" aria-label="$i18n{powerwashTitle}" - aria-describedby="powerwashSecondary"></button> - </div> - <template is="dom-if" if="[[showPowerwashDialog_]]" restamp> - <settings-powerwash-dialog on-close="onPowerwashDialogClose_"> - </settings-powerwash-dialog> - </template> + <template is="dom-if" if="[[showPowerwashDialog_]]" restamp> + <settings-powerwash-dialog on-close="onPowerwashDialogClose_"> + </settings-powerwash-dialog> + </template> +</if> +<!-- This needs to be conditioned to a feature being enabled. --> +<if expr="_google_chrome and is_win"> + <template is="dom-if" if="[[userInitiatedCleanupsEnabled_]]" restamp> + <div class="settings-box two-line" id="chromeCleanupSubpageTrigger" + on-tap="onChromeCleanupTap_" actionable> + <div class="start"> + $i18n{resetCleanupComputerTrigger} + <div class="secondary" id="chromeCleanupSecondary"> + $i18n{resetCleanupComputerTriggerDescription} + </div> + </div> + <button id="chromeCleanupArrow" is="paper-icon-button-light" + class="subpage-arrow" + aria-label="$i18n{resetCleanupComputerTrigger}" + aria-describedby="chromeCleanupSecondary"></button> + </div> + </template> +</if> + </neon-animatable> +<if expr="_google_chrome and is_win"> + <template is="dom-if" if="[[userInitiatedCleanupsEnabled_]]"> + <template is="dom-if" route-path="/cleanup"> + <settings-subpage id="chromeCleanupSubpage" + associated-control="[[$$('#chromeCleanupSubpageTrigger')]]" + page-title="$i18n{resetCleanupComputerTrigger}" + learn-more-url="$i18n{chromeCleanupLearnMoreUrl}"> + <settings-chrome-cleanup-page></settings-chrome-cleanup-page> + </settings-subpage> + </template> + </template> </if> + </settings-animated-pages> </template> <script src="reset_page.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/settings/reset_page/reset_page.js b/chromium/chrome/browser/resources/settings/reset_page/reset_page.js index 2c4d88e249c..fb5c2229223 100644 --- a/chromium/chrome/browser/resources/settings/reset_page/reset_page.js +++ b/chromium/chrome/browser/resources/settings/reset_page/reset_page.js @@ -32,11 +32,15 @@ Polymer({ value: cr.isChromeOS ? loadTimeData.getBoolean('allowPowerwash') : false }, + // <if expr="_google_chrome and is_win"> /** @private */ - showResetProfileDialog_: { + userInitiatedCleanupsEnabled_: { type: Boolean, - value: false, + value: function() { + return loadTimeData.getBoolean('userInitiatedCleanupsEnabled'); + }, }, + // </if> }, /** @@ -45,9 +49,12 @@ Polymer({ * @protected */ currentRouteChanged: function(route) { - this.showResetProfileDialog_ = - route == settings.routes.TRIGGERED_RESET_DIALOG || - route == settings.routes.RESET_DIALOG; + if (route == settings.routes.TRIGGERED_RESET_DIALOG || + route == settings.routes.RESET_DIALOG) { + /** @type {!SettingsResetProfileDialogElement} */ ( + this.$.resetProfileDialog.get()) + .show(); + } }, /** @private */ @@ -78,4 +85,11 @@ Polymer({ cr.ui.focusWithoutInk(assert(this.$.powerwashArrow)); }, // </if> + + // <if expr="_google_chrome and is_win"> + onChromeCleanupTap_: function() { + settings.navigateTo(settings.routes.CHROME_CLEANUP); + }, + // </if> + }); diff --git a/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.js b/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.js index 40827a89e20..b7c6519625c 100644 --- a/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.js +++ b/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.js @@ -41,7 +41,7 @@ Polymer({ }, }, - /** @private {!settings.ResetBrowserProxy} */ + /** @private {?settings.ResetBrowserProxy} */ browserProxy_: null, /** @@ -87,12 +87,12 @@ Polymer({ /** @private */ showDialog_: function() { - this.$.dialog.showModal(); + if (!this.$.dialog.open) + this.$.dialog.showModal(); this.browserProxy_.onShowResetProfileDialog(); }, - /** @override */ - attached: function() { + show: function() { this.isTriggered_ = settings.getCurrentRoute() == settings.routes.TRIGGERED_RESET_DIALOG; if (this.isTriggered_) { diff --git a/chromium/chrome/browser/resources/settings/route.js b/chromium/chrome/browser/resources/settings/route.js index 20b8d4b29a7..d6bf83c237b 100644 --- a/chromium/chrome/browser/resources/settings/route.js +++ b/chromium/chrome/browser/resources/settings/route.js @@ -20,10 +20,12 @@ * BLUETOOTH_DEVICES: (undefined|!settings.Route), * CERTIFICATES: (undefined|!settings.Route), * CHANGE_PICTURE: (undefined|!settings.Route), + * CHROME_CLEANUP: (undefined|!settings.Route), * CLEAR_BROWSER_DATA: (undefined|!settings.Route), * CLOUD_PRINTERS: (undefined|!settings.Route), * CUPS_PRINTERS: (undefined|!settings.Route), * DATETIME: (undefined|!settings.Route), + * DATETIME_TIMEZONE_SUBPAGE: (undefined|!settings.Route), * DEFAULT_BROWSER: (undefined|!settings.Route), * DETAILED_BUILD_INFO: (undefined|!settings.Route), * DEVICE: (undefined|!settings.Route), @@ -45,7 +47,6 @@ * MANAGE_PASSWORDS: (undefined|!settings.Route), * MANAGE_PROFILE: (undefined|!settings.Route), * MULTIDEVICE: (undefined|!settings.Route), - * NETWORK_CONFIG: (undefined|!settings.Route), * NETWORK_DETAIL: (undefined|!settings.Route), * ON_STARTUP: (undefined|!settings.Route), * PASSWORDS: (undefined|!settings.Route), @@ -65,6 +66,7 @@ * SITE_SETTINGS_AUTOMATIC_DOWNLOADS: (undefined|!settings.Route), * SITE_SETTINGS_BACKGROUND_SYNC: (undefined|!settings.Route), * SITE_SETTINGS_CAMERA: (undefined|!settings.Route), + * SITE_SETTINGS_CLIPBOARD: (undefined|!settings.Route), * SITE_SETTINGS_COOKIES: (undefined|!settings.Route), * SITE_SETTINGS_DATA_DETAILS: (undefined|!settings.Route), * SITE_SETTINGS_FLASH: (undefined|!settings.Route), @@ -135,7 +137,7 @@ cr.define('settings', function() { // |path| extends this route's path if it doesn't have a leading slash. // If it does have a leading slash, it's just set as the new route's URL. - var newUrl = path[0] == '/' ? path : this.path + '/' + path; + var newUrl = path[0] == '/' ? path : `${this.path}/${path}`; var route = new Route(newUrl); route.parent = this; @@ -216,7 +218,6 @@ cr.define('settings', function() { // <if expr="chromeos"> r.INTERNET = r.BASIC.createSection('/internet', 'internet'); r.INTERNET_NETWORKS = r.INTERNET.createChild('/networks'); - r.NETWORK_CONFIG = r.INTERNET.createChild('/networkConfig'); r.NETWORK_DETAIL = r.INTERNET.createChild('/networkDetail'); r.KNOWN_NETWORKS = r.INTERNET.createChild('/knownNetworks'); r.BLUETOOTH = r.BASIC.createSection('/bluetooth', 'bluetooth'); @@ -290,10 +291,9 @@ cr.define('settings', function() { r.SITE_SETTINGS_ALL = r.SITE_SETTINGS.createChild('all'); r.SITE_SETTINGS_SITE_DETAILS = r.SITE_SETTINGS_ALL.createChild('/content/siteDetails'); - } else if (loadTimeData.getBoolean('enableSiteDetails')) { + } else { // When there is no "All Sites", pressing 'back' from "Site Details" - // should return to "Content Settings". This should only occur when - // |kSiteSettings| is off and |kSiteDetails| is on. + // should return to "Content Settings". r.SITE_SETTINGS_SITE_DETAILS = r.SITE_SETTINGS.createChild('/content/siteDetails'); } @@ -308,6 +308,7 @@ cr.define('settings', function() { r.SITE_SETTINGS_BACKGROUND_SYNC = r.SITE_SETTINGS.createChild('backgroundSync'); r.SITE_SETTINGS_CAMERA = r.SITE_SETTINGS.createChild('camera'); + r.SITE_SETTINGS_CLIPBOARD = r.SITE_SETTINGS.createChild('clipboard'); r.SITE_SETTINGS_COOKIES = r.SITE_SETTINGS.createChild('cookies'); r.SITE_SETTINGS_SITE_DATA = r.SITE_SETTINGS_COOKIES.createChild('/siteData'); @@ -335,6 +336,8 @@ cr.define('settings', function() { // <if expr="chromeos"> if (pageVisibility.dateTime !== false) { r.DATETIME = r.ADVANCED.createSection('/dateTime', 'dateTime'); + r.DATETIME_TIMEZONE_SUBPAGE = + r.DATETIME.createChild('/dateTime/timeZone'); } // </if> @@ -380,6 +383,12 @@ cr.define('settings', function() { r.TRIGGERED_RESET_DIALOG = r.ADVANCED.createChild('/triggeredResetProfileSettings'); r.TRIGGERED_RESET_DIALOG.isNavigableDialog = true; + // <if expr="_google_chrome and is_win"> + // This should only be added if the feature is enabled. + if (loadTimeData.getBoolean('userInitiatedCleanupsEnabled')) { + r.CHROME_CLEANUP = r.RESET.createChild('/cleanup'); + } + // </if> } } 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 947dcbefc39..27db88365fa 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 @@ -37,18 +37,6 @@ var SearchEngine; */ var SearchEnginesInfo; -/** - * @typedef {{ - * allowed: boolean, - * enabled: boolean, - * alwaysOn: boolean, - * errorMessage: string, - * userName: string, - * historyEnabled: boolean - * }} - */ -var SearchPageHotwordInfo; - cr.define('settings', function() { /** @interface */ class SearchEnginesBrowserProxy { @@ -80,12 +68,6 @@ cr.define('settings', function() { */ validateSearchEngineInput(fieldName, fieldValue) {} - /** @return {!Promise<!SearchPageHotwordInfo>} */ - getHotwordInfo() {} - - /** @param {boolean} enabled */ - setHotwordSearchEnabled(enabled) {} - turnOnGoogleAssistant() {} } @@ -134,16 +116,6 @@ cr.define('settings', function() { } /** @override */ - getHotwordInfo() { - return cr.sendWithPromise('getHotwordInfo'); - } - - /** @override */ - setHotwordSearchEnabled(enabled) { - chrome.send('setHotwordSearchEnabled', [enabled]); - } - - /** @override */ turnOnGoogleAssistant() { chrome.send('turnOnGoogleAssistant'); } 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 e10d8141330..1e9f88f1e7c 100644 --- a/chromium/chrome/browser/resources/settings/search_page/search_page.html +++ b/chromium/chrome/browser/resources/settings/search_page/search_page.html @@ -82,62 +82,6 @@ </template> </div> - <template is="dom-if" if="[[hotwordInfo_.allowed]]"> - <!-- Hotword (OK Google) --> - <settings-toggle-button id="hotwordSearchEnable" - class="continuation indented" - pref="{{hotwordSearchEnablePref_}}" - label="$i18n{searchOkGoogleLabel}" - sub-label="[[getHotwordSearchEnableSubLabel_( - hotwordInfo_.alwaysOn)]]" - on-change="onHotwordSearchEnableChange_"> - </settings-toggle-button> - <div class="settings-row indented" - hidden$="[[!getShowHotwordSearchRetrain_(hotwordInfo_.*)]]"> - <div class="separator"></div> - <paper-button on-tap="onRetrainTap_" class="secondary-button"> - $i18n{searchOkGoogleRetrain} - </paper-button> - </div> - - <template is="dom-if" - if="[[getShowHotwordError_(hotwordInfo_.*, - hotwordSearchEnablePref_)]]"> - <div class="settings-box continuation indented"> - <iron-icon icon="settings:warning"></iron-icon> - <div inner-h-t-m-l="[[hotwordInfo_.errorMessage]]"></div> - </div> - </template> - - <template is="dom-if" if="[[hotwordInfo_.historyEnabled]]"> - <a class="settings-box two-line continuation indented inherit-color - no-outline" tabindex="-1" target="_blank" - href="$i18n{manageAudioHistoryUrl}"> - <div class="start"> - [[i18n('searchOkGoogleAudioHistoryLabel', - hotwordInfo_.userName)]] - <div class="secondary" id="audioHistorySecondary"> - $i18n{searchOkGoogleAudioHistorySubtext} - </div> - </div> - <button actionable class="icon-external" - is="paper-icon-button-light" - aria-label$="[[i18n('searchOkGoogleAudioHistoryLabel', - hotwordInfo_.userName)]]" - aria-describedby="audioHistorySecondary"></button> - </a> - </template> - </template> - -<if expr="chromeos"> - <template is="dom-if" - if="[[hotwordSearchEnablePref_.value]]"> - <div class="start settings-box continuation indented"> - $i18n{searchOkGoogleDisabled} - </div> - </template> -</if> - <!-- Manage search engines --> <div id="engines-subpage-trigger" class="settings-box" on-tap="onManageSearchEnginesTap_" actionable> 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 3dbfcf19d69..9a3d913e969 100644 --- a/chromium/chrome/browser/resources/settings/search_page/search_page.js +++ b/chromium/chrome/browser/resources/settings/search_page/search_page.js @@ -34,18 +34,6 @@ Polymer({ /** @private Filter applied to search engines. */ searchEnginesFilter_: String, - /** @private {!SearchPageHotwordInfo|undefined} */ - hotwordInfo_: Object, - - /** - * This is a local PrefObject used to reflect the enabled state of hotword - * search. It is not tied directly to a pref. (There are two prefs - * associated with state and they do not map directly to whether or not - * hotword search is actually enabled). - * @private {!chrome.settingsPrivate.PrefObject|undefined} - */ - hotwordSearchEnablePref_: Object, - /** @type {?Map<string, string>} */ focusConfig_: Object, @@ -80,15 +68,10 @@ Polymer({ // Omnibox search engine var updateSearchEngines = searchEngines => { this.set('searchEngines_', searchEngines.defaults); - this.requestHotwordInfoUpdate_(); }; this.browserProxy_.getSearchEnginesList().then(updateSearchEngines); cr.addWebUIListener('search-engines-changed', updateSearchEngines); - // Hotword (OK Google) listener - cr.addWebUIListener( - 'hotword-info-update', this.hotwordInfoUpdate_.bind(this)); - this.focusConfig_ = new Map(); if (settings.routes.SEARCH_ENGINES) { this.focusConfig_.set( @@ -139,68 +122,6 @@ Polymer({ }, // </if> - /** - * @param {!Event} event - * @private - */ - onHotwordSearchEnableChange_: function(event) { - // Do not set the pref directly, allow Chrome to run the setup app instead. - this.browserProxy_.setHotwordSearchEnabled( - !!this.hotwordSearchEnablePref_.value); - }, - - /** @private */ - requestHotwordInfoUpdate_: function() { - this.browserProxy_.getHotwordInfo().then(hotwordInfo => { - this.hotwordInfoUpdate_(hotwordInfo); - }); - }, - - /** - * @param {!SearchPageHotwordInfo} hotwordInfo - * @private - */ - hotwordInfoUpdate_: function(hotwordInfo) { - this.hotwordInfo_ = hotwordInfo; - this.hotwordSearchEnablePref_ = { - key: 'unused', // required for PrefObject - type: chrome.settingsPrivate.PrefType.BOOLEAN, - value: this.hotwordInfo_.enabled, - }; - }, - - /** - * @return {string} - * @private - */ - getHotwordSearchEnableSubLabel_: function() { - return this.i18n( - this.hotwordInfo_.alwaysOn ? 'searchOkGoogleSubtextAlwaysOn' : - 'searchOkGoogleSubtextNoHardware'); - }, - - /** - * @return {boolean} - * @private - */ - getShowHotwordSearchRetrain_: function() { - return this.hotwordInfo_.enabled && this.hotwordInfo_.alwaysOn; - }, - - /** - * @return {boolean} True if the pref is enabled but hotword is not. - * @private - */ - getShowHotwordError_: function() { - return this.hotwordInfo_.enabled && !!this.hotwordInfo_.errorMessage; - }, - - /** @private */ - onRetrainTap_: function() { - // Re-enable hotword search enable; this will trigger the retrain UI. - this.browserProxy_.setHotwordSearchEnabled(this.hotwordInfo_.enabled); - }, - // <if expr="chromeos"> /** * @param {boolean} toggleValue diff --git a/chromium/chrome/browser/resources/settings/search_settings.js b/chromium/chrome/browser/resources/settings/search_settings.js index b05aa1106f4..0b89de713a7 100644 --- a/chromium/chrome/browser/resources/settings/search_settings.js +++ b/chromium/chrome/browser/resources/settings/search_settings.js @@ -517,7 +517,7 @@ cr.define('settings', function() { // problematic for regular expressions. var searchText = this.rawQuery_.trim().replace(SANITIZE_REGEX, '\\$&'); if (searchText.length > 0) - regExp = new RegExp('(' + searchText + ')', 'i'); + regExp = new RegExp(`(${searchText})`, 'i'); return regExp; } diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_section.html b/chromium/chrome/browser/resources/settings/settings_page/settings_section.html index 17101c8f335..644879a6d93 100644 --- a/chromium/chrome/browser/resources/settings/settings_page/settings_section.html +++ b/chromium/chrome/browser/resources/settings/settings_page/settings_section.html @@ -20,9 +20,7 @@ } #header .title { - color: var(--paper-grey-700); - font-size: 100%; - font-weight: 500; + @apply(--cr-section-text); margin-bottom: 0; margin-top: var(--settings-page-vertical-margin); } diff --git a/chromium/chrome/browser/resources/settings/settings_resources.grd b/chromium/chrome/browser/resources/settings/settings_resources.grd index 93c5edf1a72..f5cc6960361 100644 --- a/chromium/chrome/browser/resources/settings/settings_resources.grd +++ b/chromium/chrome/browser/resources/settings/settings_resources.grd @@ -681,6 +681,12 @@ <structure name="IDR_SETTINGS_PASSWORD_EDIT_DIALOG_JS" file="passwords_and_forms_page/password_edit_dialog.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_PASSWORDS_EXPORT_DIALOG_HTML" + file="passwords_and_forms_page/passwords_export_dialog.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_PASSWORDS_EXPORT_DIALOG_JS" + file="passwords_and_forms_page/passwords_export_dialog.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_PEOPLE_PAGE_HTML" file="people_page/people_page.html" type="chrome_html" @@ -813,6 +819,14 @@ file="printing_page/cups_add_printer_dialog_util.js" type="chrome_html" /> </if> + <if expr="not chromeos"> + <structure name="IDR_SETTINGS_PRINTING_BROWSER_PROXY_HTML" + file="printing_page/printing_browser_proxy.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_PRINTING_BROWSER_PROXY_JS" + file="printing_page/printing_browser_proxy.js" + type="chrome_html" /> + </if> <structure name="IDR_SETTINGS_CLOUD_PRINTING_PAGE_HTML" file="printing_page/cloud_printers.html" type="chrome_html" /> @@ -856,9 +870,11 @@ type="chrome_html" /> <structure name="IDR_SETTINGS_PROTOCOL_HANDLERS_HTML" file="site_settings/protocol_handlers.html" + preprocess="true" type="chrome_html" /> <structure name="IDR_SETTINGS_PROTOCOL_HANDLERS_JS" file="site_settings/protocol_handlers.js" + preprocess="true" type="chrome_html" /> <structure name="IDR_SETTINGS_ROUTE_HTML" file="route.html" @@ -909,6 +925,7 @@ type="chrome_html" /> <structure name="IDR_SETTINGS_SITE_SETTINGS_PREFS_BROWSER_PROXY_JS" file="site_settings/site_settings_prefs_browser_proxy.js" + preprocess="true" type="chrome_html" /> <structure name="IDR_SETTINGS_SITE_DETAILS_HTML" file="site_settings/site_details.html" @@ -1105,6 +1122,24 @@ <structure name="IDR_SETTINGS_DATE_TIME_PAGE_JS" file="date_time_page/date_time_page.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_DATE_TIME_TYPES_HTML" + file="date_time_page/date_time_types.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_DATE_TIME_TYPES_JS" + file="date_time_page/date_time_types.js" + type="chrome_html" /> + <structure name="IDR_SETTINGS_TIMEZONE_SELECTOR_HTML" + file="date_time_page/timezone_selector.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_TIMEZONE_SELECTOR_JS" + file="date_time_page/timezone_selector.js" + type="chrome_html" /> + <structure name="IDR_SETTINGS_TIMEZONE_SUBPAGE_HTML" + file="date_time_page/timezone_subpage.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_TIMEZONE_SUBPAGE_JS" + file="date_time_page/timezone_subpage.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_INTERNET_CONFIG_HTML" file="internet_page/internet_config.html" type="chrome_html" /> @@ -1123,6 +1158,12 @@ <structure name="IDR_SETTINGS_INTERNET_KNOWN_NETWORKS_PAGE_JS" file="internet_page/internet_known_networks_page.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_INTERNET_PAGE_BROWSER_PROXY_HTML" + file="internet_page/internet_page_browser_proxy.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_INTERNET_PAGE_BROWSER_PROXY_JS" + file="internet_page/internet_page_browser_proxy.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_INTERNET_PAGE_HTML" file="internet_page/internet_page.html" type="chrome_html" /> @@ -1150,12 +1191,6 @@ <structure name="IDR_SETTINGS_NETWORK_PROXY_SECTION_JS" file="internet_page/network_proxy_section.js" type="chrome_html" /> - <structure name="IDR_SETTINGS_NETWORK_SIMINFO_HTML" - file="internet_page/network_siminfo.html" - type="chrome_html" /> - <structure name="IDR_SETTINGS_NETWORK_SIMINFO_JS" - file="internet_page/network_siminfo.js" - type="chrome_html" /> <structure name="IDR_SETTINGS_NETWORK_SUMMARY_HTML" file="internet_page/network_summary.html" type="chrome_html" /> 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 0b217e7ada0..7aca114c1af 100644 --- a/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html +++ b/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html @@ -39,7 +39,6 @@ cr-toolbar { @apply(--layout-center); - --cr-toolbar-field-width: var(--settings-card-max-width); --iron-icon-fill-color: white; background-color: var(--google-blue-700); color: white; 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 08f2e59a2fa..a73e3303987 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 @@ -108,6 +108,7 @@ Polymer({ break; case settings.ContentSettingsTypes.AUTOMATIC_DOWNLOADS: case settings.ContentSettingsTypes.CAMERA: + case settings.ContentSettingsTypes.CLIPBOARD: case settings.ContentSettingsTypes.GEOLOCATION: case settings.ContentSettingsTypes.MIC: case settings.ContentSettingsTypes.NOTIFICATIONS: 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 fbc1a37e421..952595bdf69 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/compiled_resources2.gyp +++ b/chromium/chrome/browser/resources/settings/site_settings/compiled_resources2.gyp @@ -88,6 +88,7 @@ '<(DEPTH)/ui/webui/resources/cr_elements/cr_action_menu/compiled_resources2.gyp:cr_action_menu', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', 'site_settings_behavior', + '../android_apps_page/compiled_resources2.gyp:android_apps_browser_proxy', ], '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 44fbc1546d1..468d0b9fff3 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/constants.js +++ b/chromium/chrome/browser/resources/settings/site_settings/constants.js @@ -32,6 +32,7 @@ settings.ContentSettingsTypes = { ZOOM_LEVELS: 'zoom-levels', PROTECTED_CONTENT: 'protectedContent', ADS: 'ads', + CLIPBOARD: 'clipboard', }; /** @@ -56,6 +57,8 @@ settings.ContentSetting = { * @enum {string} */ settings.SiteSettingSource = { + ADS_BLOCKED: 'ads-blocked', + ADS_FILTER_BLACKLIST: 'ads-filter-blacklist', DEFAULT: 'default', // This source is for the Protected Media Identifier / Protected Content // content setting only, which is only available on ChromeOS. diff --git a/chromium/chrome/browser/resources/settings/site_settings/cookie_info.js b/chromium/chrome/browser/resources/settings/site_settings/cookie_info.js index 43056bb906f..c41527432e4 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/cookie_info.js +++ b/chromium/chrome/browser/resources/settings/site_settings/cookie_info.js @@ -61,6 +61,8 @@ var CookieDataForDisplay; ['origin', 'serviceWorkerOrigin'], ['size', 'serviceWorkerSize'], ['scopes', 'serviceWorkerScopes'] ], + 'shared_worker': + [['worker', 'sharedWorkerWorker'], ['name', 'sharedWorkerName']], 'cache_storage': [ ['origin', 'cacheStorageOrigin'], ['size', 'cacheStorageSize'], ['modified', 'cacheStorageLastModified'] diff --git a/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js b/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js index 28d4a1dccce..224b4ba722d 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js @@ -28,7 +28,6 @@ var LocalDataItem; * TODO(dschuyler): add |filter| and |order|. * @typedef {{ * items: !Array<!LocalDataItem>, - * start: number, * total: number, * }} */ @@ -39,13 +38,9 @@ cr.define('settings', function() { 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) {} + getDisplayList(filter) {} /** * Removes all local data (local storage, cookies, etc.). @@ -93,9 +88,8 @@ cr.define('settings', function() { */ class LocalDataBrowserProxyImpl { /** @override */ - getDisplayList(filter, begin, count) { - return cr.sendWithPromise( - 'localData.getDisplayList', filter, begin, count); + getDisplayList(filter) { + return cr.sendWithPromise('localData.getDisplayList', filter); } /** @override */ diff --git a/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html b/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html index 04de6344da3..a37356e6dda 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html +++ b/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html @@ -1,10 +1,10 @@ <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/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.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_toggle/cr_toggle.html"> +<link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="site_settings_behavior.html"> @@ -27,9 +27,9 @@ <div id="categoryLabel" class="start"> [[computeHandlersDescription_(categoryEnabled)]] </div> - <paper-toggle-button id="toggle" checked="{{categoryEnabled}}" + <cr-toggle id="toggle" checked="{{categoryEnabled}}" on-change="onToggleChange_" aria-labelledby="categoryLabel"> - </paper-toggle-button> + </cr-toggle> </div> <template is="dom-repeat" items="[[protocols]]" as="protocol"> @@ -67,6 +67,18 @@ $i18n{handlerRemove} </button> </dialog> + +<if expr="chromeos"> + <template is="dom-if" if="[[settingsAppAvailable_]]"> + <div class="settings-box first" + on-tap="onManageAndroidAppsTap_" actionable> + <div class="start"> + <div>$i18n{androidAppsManageAppLinks}</div> + </div> + <button class="icon-external" is="paper-icon-button-light"></button> + </div> + </template> +</if> </template> <script src="protocol_handlers.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.js b/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.js index 871659b8eb8..7da371a9afe 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.js +++ b/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.js @@ -59,6 +59,14 @@ Polymer({ /* Labels for the toggle on/off positions. */ toggleOffLabel: String, toggleOnLabel: String, + + // <if expr="chromeos"> + /** @private */ + settingsAppAvailable_: { + type: Boolean, + value: false, + }, + // </if> }, /** @override */ @@ -73,6 +81,29 @@ Polymer({ this.browserProxy.observeProtocolHandlers(); }, + // <if expr="chromeos"> + /** @override */ + attached: function() { + if (settings.AndroidAppsBrowserProxyImpl) { + cr.addWebUIListener( + 'android-apps-info-update', this.androidAppsInfoUpdate_.bind(this)); + settings.AndroidAppsBrowserProxyImpl.getInstance() + .requestAndroidAppsInfo(); + } + }, + // </if> + + // <if expr="chromeos"> + /** + * Receives updates on whether or not ARC settings app is available. + * @param {AndroidAppsInfo} info + * @private + */ + androidAppsInfoUpdate_: function(info) { + this.settingsAppAvailable_ = info.settingsAppAvailable; + }, + // </if> + /** * Obtains the description for the main toggle. * @return {string} The description to use. @@ -177,5 +208,15 @@ Polymer({ .showAt( /** @type {!Element} */ ( Polymer.dom(/** @type {!Event} */ (event)).localTarget)); - } + }, + + // <if expr="chromeos"> + /** + * Opens an activity to handle App links (preferred apps). + * @private + */ + onManageAndroidAppsTap_: function() { + this.browserProxy.showAndroidManageAppLinks(); + }, + // </if> }); 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 eb7d2da4117..72c73a87961 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_data.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_data.html @@ -9,6 +9,7 @@ <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="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.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"> @@ -22,6 +23,16 @@ border-top: none; } + paper-spinner-lite { + @apply(--cr-icon-height-width); + opacity: 0; + transition-delay: 1s; + } + + paper-spinner-lite[active] { + opacity: 1; + } + #removeShowingSites { -webkit-margin-start: auto; } @@ -31,7 +42,9 @@ } </style> <div class="settings-box continuation"> - <paper-button class="secondary-button" id="removeShowingSites" + <paper-spinner-lite active="[[isLoading_]]"></paper-spinner-lite> + <paper-button class="secondary-button" + disabled$="[[isLoading_]]" id="removeShowingSites" on-tap="onRemoveShowingSitesTap_" hidden$="[[!sites.length]]"> [[computeRemoveLabel_(filter)]] </paper-button> 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 097465dbc0a..e0e85e99d8b 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_data.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_data.js @@ -60,6 +60,8 @@ Polymer({ observer: 'focusConfigChanged_', }, + isLoading_: Boolean, + /** @type {!Array<!LocalDataItem>} */ sites: { type: Array, @@ -99,6 +101,7 @@ Polymer({ settings.GlobalScrollTargetBehaviorImpl.currentRouteChanged.call( this, currentRoute); if (currentRoute == settings.routes.SITE_SETTINGS_SITE_DATA) { + this.isLoading_ = true; this.browserProxy_.reloadCookies().then(this.updateSiteList_.bind(this)); } }, @@ -138,12 +141,12 @@ Polymer({ * @private */ updateSiteList_: function() { - this.browserProxy_ - .getDisplayList(this.filter, 0 /* start */, -1 /* count */) - .then((listInfo) => { - this.sites = listInfo.items; - this.fire('site-data-list-complete'); - }); + this.isLoading_ = true; + this.browserProxy_.getDisplayList(this.filter).then((listInfo) => { + this.sites = listInfo.items; + this.isLoading_ = false; + this.fire('site-data-list-complete'); + }); }, /** 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 8e8b802b64a..b749a1f87d4 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 @@ -15,6 +15,7 @@ var categoryLabels = { indexed_db: loadTimeData.getString('cookieDatabaseStorage'), local_storage: loadTimeData.getString('cookieLocalStorage'), service_worker: loadTimeData.getString('cookieServiceWorker'), + shared_worker: loadTimeData.getString('cookieSharedWorker'), media_license: loadTimeData.getString('cookieMediaLicense'), }; 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 4f903171bb8..9a10a9ebf6b 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_details.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_details.html @@ -107,6 +107,12 @@ icon="cr:open-in-new" id="popups" label="$i18n{siteSettingsPopups}"> </site-details-permission> <site-details-permission + category="{{ContentSettingsTypes.ADS}}" + icon="settings:ads" id="ads" + label="$i18n{siteSettingsAds}" + hidden$="[[!enableSafeBrowsingSubresourceFilter_]]"> + </site-details-permission> + <site-details-permission category="{{ContentSettingsTypes.BACKGROUND_SYNC}}" icon="settings:sync" id="backgroundSync" label="$i18n{siteSettingsBackgroundSync}"> @@ -126,6 +132,12 @@ id="midiDevices" label="$i18n{siteSettingsMidiDevices}"> </site-details-permission> <site-details-permission + category="{{ContentSettingsTypes.CLIPBOARD}}" + icon="settings:clipboard" id="clipboard" + label="$i18n{siteSettingsClipboard}" + hidden$="[[!enableClipboardContentSetting_]]"> + </site-details-permission> + <site-details-permission category="{{ContentSettingsTypes.UNSANDBOXED_PLUGINS}}" icon="cr:extension" id="unsandboxedPlugins" label="$i18n{siteSettingsUnsandboxedPlugins}"> 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 cc2e9aec5c9..b7fe492082f 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_details.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_details.js @@ -51,6 +51,14 @@ Polymer({ }, /** @private */ + enableSafeBrowsingSubresourceFilter_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('enableSafeBrowsingSubresourceFilter'); + }, + }, + + /** @private */ enableSoundContentSetting_: { type: Boolean, value: function() { @@ -58,6 +66,14 @@ Polymer({ }, }, + /** @private */ + enableClipboardContentSetting_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('enableClipboardContentSetting'); + }, + }, + /** * The type of storage for the origin. * @private @@ -125,8 +141,9 @@ Polymer({ */ onPermissionChanged_: function(category, origin, embeddingOrigin) { if (this.origin === undefined || this.origin == '' || - origin === undefined || origin == '') + origin === undefined || origin == '') { return; + } if (!this.getCategoryList_().includes(category)) return; 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 c55f5716acf..fdf7d369701 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 @@ -15,7 +15,8 @@ <style include="settings-shared md-select"></style> <div id="details"> <div id="permissionItem" - class$="list-item [[permissionSourceStringClass_(site.source)]]"> + class$="list-item [[permissionInfoStringClass_(site.source, category, + site.setting)]]"> <div> <iron-icon icon="[[icon]]"> </iron-icon> @@ -23,9 +24,14 @@ <div class="middle" id="permissionHeader"> [[label]] <div class="secondary" - hidden$="[[!hasPermissionSourceString_(site.source)]]" - inner-h-t-m-l="[[permissionSourceString_( + hidden$="[[!hasPermissionInfoString_(site.source, category, + site.setting)]]" + inner-h-t-m-l="[[permissionInfoString_( site.source, + category, + site.setting, + '$i18nPolymer{siteSettingsSourceAdsBlacklist}', + '$i18nPolymer{siteSettingsAdsBlockSingular}', '$i18nPolymer{siteSettingsSourceEmbargo}', '$i18nPolymer{siteSettingsSourceInsecureOrigin}', '$i18nPolymer{siteSettingsSourceKillSwitch}', @@ -41,8 +47,10 @@ <select id="permission" class="md-select" aria-labelledby="permissionHeader" on-change="onPermissionSelectionChange_" - disabled$="[[!isPermissionUserControlled_(site.source)]]"> - <option id="default" value$="[[ContentSetting.DEFAULT]]"> + disabled$="[[!isPermissionUserControlled_(site.source, category, + site.setting)]]"> + <option id="default" value$="[[ContentSetting.DEFAULT]]" + hidden$="[[isAdsCategory_(category)]]"> [[defaultSettingString_( defaultSetting_, '$i18nPolymer{siteSettingsActionAskDefault}', 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 27c34c2dce2..e01a47a8478 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 @@ -118,38 +118,52 @@ Polymer({ }, /** - * Returns true if there's a string to display that describes the source of - * this permission's setting. Currently, this only gets called when + * Returns true if there's a string to display that provides more information + * about this permission's setting. Currently, this only gets called when * |this.site| is updated. * @param {!settings.SiteSettingSource} source The source of the permission. + * @param {!settings.ContentSettingsTypes} category The permission type. + * @param {!settings.ContentSetting} setting The permission setting. + * @return {boolean} Whether the permission will have a source string to + * display. * @private */ - hasPermissionSourceString_: function(source) { + hasPermissionInfoString_: function(source, category, setting) { return ( source != settings.SiteSettingSource.DEFAULT && source != settings.SiteSettingSource.PREFERENCE); }, /** - * Checks if there's a permission source string to display, and returns the + * Checks if there's a additional information to display, and returns the * class name to apply to permissions if so. + * @param {!settings.SiteSettingSource} source The source of the permission. + * @param {!settings.ContentSettingsTypes} category The permission type. + * @param {!settings.ContentSetting} setting The permission setting. * @return {string} CSS class applied when there is an additional description * string. * @private */ - permissionSourceStringClass_: function(source) { - return this.hasPermissionSourceString_(source) ? 'two-line' : ''; + permissionInfoStringClass_: function(source, category, setting) { + return this.hasPermissionInfoString_(source, category, setting) ? + 'two-line' : + ''; }, /** - * Returns true if this permission's source is controlled by the user. + * Returns true if this permission can be controlled by the user. + * @param {!settings.SiteSettingSource} source The source of the permission. + * @param {!settings.ContentSettingsTypes} category The permission type. + * @param {!settings.ContentSetting} setting The permission setting. * @return {boolean} * @private */ - isPermissionUserControlled_: function(source) { - // Users are able override embargo. - return !this.hasPermissionSourceString_(source) || - source == settings.SiteSettingSource.EMBARGO; + isPermissionUserControlled_: function(source, category, setting) { + // Users are able override embargo and ads blacklisting. + return !this.hasPermissionInfoString_(source, category, setting) || + source == settings.SiteSettingSource.EMBARGO || + source == settings.SiteSettingSource.ADS_FILTER_BLACKLIST || + source == settings.SiteSettingSource.ADS_BLOCKED; }, /** @@ -173,9 +187,25 @@ Polymer({ }, /** - * Updates the string used to describe the source of this permission setting. + * Returns true if this permission is the Ads permission. + * @param {!settings.ContentSettingsTypes} category The permission type. + * @return {boolean} + * @private + */ + isAdsCategory_: function(category) { + return category == settings.ContentSettingsTypes.ADS; + }, + + /** + * Updates the information string for the current permission. * Currently, this only gets called when |this.site| is updated. * @param {!settings.SiteSettingSource} source The source of the permission. + * @param {!settings.ContentSettingsTypes} category The permission type. + * @param {!settings.ContentSetting} setting The permission setting. + * @param {!string} adsBlacklistString The string to show if the site is + * blacklisted for showing bad ads. + * @param {!string} adsBlockString The string to show if ads are blocked, but + * the site is not blacklisted. * @param {!string} embargoString * @param {!string} insecureOriginString * @param {!string} killSwitchString @@ -187,8 +217,9 @@ Polymer({ * @param {!string} policyAskString * @private */ - permissionSourceString_: function( - source, embargoString, insecureOriginString, killSwitchString, + permissionInfoString_: function( + source, category, setting, adsBlacklistString, adsBlockString, + embargoString, insecureOriginString, killSwitchString, extensionAllowString, extensionBlockString, extensionAskString, policyAllowString, policyBlockString, policyAskString) { @@ -204,12 +235,25 @@ Polymer({ policyStrings[settings.ContentSetting.BLOCK] = policyBlockString; policyStrings[settings.ContentSetting.ASK] = policyAskString; - if (source == settings.SiteSettingSource.DRM_DISABLED) { + if (source == settings.SiteSettingSource.ADS_FILTER_BLACKLIST) { + assert( + settings.ContentSettingsTypes.ADS == category, + 'The ads filter blacklist only applies to Ads.'); + return adsBlacklistString; + } else if (source == settings.SiteSettingSource.ADS_BLOCKED) { assert( - settings.ContentSetting.BLOCK == this.site.setting, + settings.ContentSettingsTypes.ADS == category, + 'The Ads user-blocked source only applies to Ads.'); + assert( + settings.ContentSetting.ALLOW != setting, + 'The Ads setting must be blocked for this source.'); + return adsBlockString; + } else if (source == settings.SiteSettingSource.DRM_DISABLED) { + assert( + settings.ContentSetting.BLOCK == setting, 'If DRM is disabled, Protected Content must be blocked.'); assert( - settings.ContentSettingsTypes.PROTECTED_CONTENT == this.category, + settings.ContentSettingsTypes.PROTECTED_CONTENT == category, 'The DRM disabled source only applies to Protected Content.'); return this.i18nAdvanced('siteSettingsSourceDrmDisabled', { substitutions: @@ -217,29 +261,28 @@ Polymer({ }); } else if (source == settings.SiteSettingSource.EMBARGO) { assert( - settings.ContentSetting.BLOCK == this.site.setting, + settings.ContentSetting.BLOCK == setting, 'Embargo is only used to block permissions.'); return embargoString; } else if (source == settings.SiteSettingSource.EXTENSION) { - return extensionStrings[this.site.setting]; + return extensionStrings[setting]; } else if (source == settings.SiteSettingSource.INSECURE_ORIGIN) { assert( - settings.ContentSetting.BLOCK == this.site.setting, + settings.ContentSetting.BLOCK == setting, 'Permissions can only be blocked due to insecure origins.'); return insecureOriginString; } else if (source == settings.SiteSettingSource.KILL_SWITCH) { assert( - settings.ContentSetting.BLOCK == this.site.setting, + settings.ContentSetting.BLOCK == setting, 'The permissions kill switch can only be used to block permissions.'); return killSwitchString; } else if (source == settings.SiteSettingSource.POLICY) { - return policyStrings[this.site.setting]; + return policyStrings[setting]; } else if ( source == settings.SiteSettingSource.DEFAULT || source == settings.SiteSettingSource.PREFERENCE) { return ''; } - assertNotReached( - `No string for ${this.category} setting source '${source}'`); + assertNotReached(`No string for ${category} setting source '${source}'`); }, }); 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 6ab9c0a38a5..e19f90cfec8 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 @@ -266,6 +266,14 @@ cr.define('settings', function() { * @param {string} host The host to remove zoom levels for. */ removeZoomLevel(host) {} + + // <if expr="chromeos"> + /** + * Links to com.android.settings.Settings$ManageDomainUrlsActivity on ARC + * side, this is to manage app preferences. + */ + showAndroidManageAppLinks() {} + // </if> } /** @@ -386,6 +394,13 @@ cr.define('settings', function() { removeZoomLevel(host) { chrome.send('removeZoomLevel', [host]); } + + // <if expr="chromeos"> + /** @override */ + showAndroidManageAppLinks() { + chrome.send('showAndroidManageAppLinks'); + } + // </if> } // The singleton instance_ is replaced with a test version of this wrapper diff --git a/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html b/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html index 7388b9f6fd2..0d49492c635 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html +++ b/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html @@ -21,7 +21,7 @@ } </style> - <div class="settings-box" hidden$="[[hasDevices_(devices_)]]"> + <div class="settings-box first" hidden$="[[hasDevices_(devices_)]]"> $i18n{noUsbDevicesFound} </div> <template is="dom-repeat" items="[[devices_]]"> diff --git a/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html index b0d1207994a..c523b2f8536 100644 --- a/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html +++ b/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html @@ -234,8 +234,8 @@ <div class="secondary" id="soundSecondary"> [[defaultSettingLabel_( default_.sound, - '$i18nPolymer{siteSettingsAllowed}', - '$i18nPolymer{siteSettingsBlocked}')]] + '$i18nPolymer{siteSettingsSoundAllow}', + '$i18nPolymer{siteSettingsSoundBlock}')]] </div> </div> <button class="subpage-arrow" is="paper-icon-button-light" @@ -317,6 +317,26 @@ aria-label="$i18n{siteSettingsMidiDevices}" aria-describedby="midiDevicesSecondary"></button> </div> + <template is="dom-if" if="[[enableClipboardContentSetting_]]"> + <div id="clipboard" class="settings-box two-line" + category$="[[ContentSettingsTypes.CLIPBOARD]]" + data-route="SITE_SETTINGS_CLIPBOARD" on-tap="onTapNavigate_" + actionable> + <iron-icon icon="settings:clipboard"></iron-icon> + <div class="middle"> + $i18n{siteSettingsClipboard} + <div class="secondary" id="clipboardSecondary"> + [[defaultSettingLabel_( + default_.clipboard, + '$i18nPolymer{siteSettingsAskBeforeAccessing}', + '$i18nPolymer{siteSettingsBlocked}')]] + </div> + </div> + <button class="subpage-arrow" is="paper-icon-button-light" + aria-label="$i18n{siteSettingsClipboard}" + aria-describedby="clipboardSecondary"></button> + </div> + </template> <div id="zoom-levels" class="settings-box" category$="[[ContentSettingsTypes.ZOOM_LEVELS]]" data-route="SITE_SETTINGS_ZOOM_LEVELS" on-tap="onTapNavigate_" 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 e3354d023cb..cc72fa6d4fa 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 @@ -52,6 +52,14 @@ Polymer({ }, /** @private */ + enableClipboardContentSetting_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('enableClipboardContentSetting'); + } + }, + + /** @private */ enableSoundContentSetting_: { type: Boolean, value: function() { @@ -96,6 +104,7 @@ Polymer({ [R.SITE_SETTINGS_USB_DEVICES, 'usb-devices'], [R.SITE_SETTINGS_PDF_DOCUMENTS, 'pdf-documents'], [R.SITE_SETTINGS_PROTECTED_CONTENT, 'protected-content'], + [R.SITE_SETTINGS_CLIPBOARD, "clipboard"], ].forEach(pair => { var route = pair[0]; var id = pair[1]; diff --git a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.css b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.css new file mode 100644 index 00000000000..a7102e677a0 --- /dev/null +++ b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.css @@ -0,0 +1,322 @@ +/* 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. */ + +.picture img { + border-radius: 50%; + max-height: 100%; + max-width: 100%; +} + +.details { + padding: 0 24px; +} + +#picture-container { + align-items: center; + display: flex; + justify-content: center; + padding-bottom: 32px; + padding-top: 24px; +} + +.picture { + -webkit-margin-start: 84px; + height: 96px; + position: relative; + width: 96px; +} + +#profile-picture, +.checkmark-circle { + position: absolute; +} + +.message-container { + display: flex; + margin-bottom: 16px; +} + +.message-container:last-child { + margin-bottom: 32px; +} + +.message-container .logo { + -webkit-margin-end: 20px; + background-size: cover; + flex-shrink: 0; + height: 20px; + position: relative; + top: -2px; + width: 20px; +} + +#chrome-logo { + background-image: url(../../../../../ui/webui/resources/images/200-logo_chrome.png); +} + +#googleg-logo { + background-image: url(../../../../../ui/webui/resources/images/200-logo_googleg.png); +} + +.message-container .title { + font-weight: 500; + margin-bottom: 4px; +} + +.message-container .body { + color: #646464; +} + +.message-container .text { + line-height: 20px; +} + +.message-container #activityControlsCheckbox { + -webkit-margin-start: 40px; +} + +#undoButton { + -webkit-margin-start: 8px; +} + +#syncDisabledDetails { + line-height: 20px; + margin-bottom: 8px; + margin-top: 16px; + padding: 0 24px; +} + +#illustration { + height: 96px; + margin: 0 auto; + position: relative; + width: 264px; +} + +#checkmark-circle { + background: rgb(66, 133, 244); + border: 2px solid #fff; + border-radius: 50%; + bottom: 0; + height: 24px; + position: absolute; + right: 0; + transform: scale(0); + width: 24px; +} + +.loaded #checkmark-circle { + animation: scale-circle 300ms cubic-bezier(0, 0, 0.2, 1) forwards; +} + +@keyframes scale-circle { + from { transform: scale(0); } + to { transform: scale(1); } +} + +#checkmark-check { + left: 5px; + position: absolute; + top: 7px; +} + +.loaded #checkmark-path { + animation: draw-path 300ms cubic-bezier(0, 0, 0.2, 1) 100ms forwards; +} + +@keyframes draw-path { + from { stroke-dashoffset: 16; } + to { stroke-dashoffset: 0; } +} + +#icons { + height: 96px; + position: absolute; + width: 264px; +} + +#icons > div { + animation-delay: 200ms; + animation-duration: 1.4s; + animation-fill-mode: forwards; + animation-timing-function: cubic-bezier(0.25, 0.45, 0.4, 0.7); + background-size: cover; + opacity: 0; + position: absolute; +} + +#icon-bookmarks { + background: url(../../../../../ui/webui/resources/images/icon_bookmarks.svg); + height: 36px; + left: 58px; + top: 0; + width: 36px; +} + +#icon-extensions { + background: url(../../../../../ui/webui/resources/images/icon_extensions.svg); + height: 24px; + left: 30px; + top: 30px; + width: 24px; +} + + +#icon-passwords { + background: url(../../../../../ui/webui/resources/images/icon_passwords.svg); + height: 30px; + left: 38px; + top: 66px; + width: 40px; +} + +#icon-history { + background: url(../../../../../ui/webui/resources/images/icon_history.svg); + height: 36px; + left: 190px; + top: 6px; + width: 36px; +} + +#icon-tabs { + background: url(../../../../../ui/webui/resources/images/icon_tabs.svg); + height: 24px; + left: 222px; + top: 44px; + width: 24px; +} + +#icon-themes { + background: url(../../../../../ui/webui/resources/images/icon_themes.svg); + height: 30px; + left: 184px; + top: 62px; + width: 32px; +} + +#icon-circle-open { + border: 2px solid #000; + border-radius: 50%; + height: 8px; + left: 6px; + top: 56px; + width: 8px; +} + +.icon-circle { + background: #000; + border-radius: 50%; + height: 4px; + width: 4px; +} + +#icon-circle-1 { + left: 64px; + top: 50px; +} + +#icon-circle-2 { + left: 178px; + top: 18px; +} + +#icon-circle-3 { + left: 194px; + top: 50px; +} + +#icon-circle-4 { + left: 258px; + top: 36px; +} + +.loaded .fade-top-left { + animation-name: fade-in-icon-top-left; +} + +.loaded .fade-top-right { + animation-name: fade-in-icon-top-right; +} + +.loaded .fade-middle-left { + animation-name: fade-in-icon-middle-left; +} + +.loaded .fade-middle-right { + animation-name: fade-in-icon-middle-right; +} + +.loaded .fade-bottom-left { + animation-name: fade-in-icon-bottom-left; +} + +.loaded .fade-bottom-right { + animation-name: fade-in-icon-bottom-right; +} + +@keyframes fade-in-icon-top-left { + from { + opacity: 0; + transform: translate(0, 0); + } + to { + opacity: 0.1; + transform: translate(-4px, -4px); + } +} + +@keyframes fade-in-icon-top-right { + from { + opacity: 0; + transform: translate(0, 0); + } + to { + opacity: 0.1; + transform: translate(4px, -4px); + } +} + +@keyframes fade-in-icon-middle-left { + from { + opacity: 0; + transform: translate(0, 0); + } + to { + opacity: 0.1; + transform: translate(-4px, 0); + } +} + +@keyframes fade-in-icon-middle-right { + from { + opacity: 0; + transform: translate(0, 0); + } + to { + opacity: 0.1; + transform: translate(4px, 0); + } +} + +@keyframes fade-in-icon-bottom-left { + from { + opacity: 0; + transform: translate(0, 0); + } + to { + opacity: 0.1; + transform: translate(-4px, 4px); + } +} + +@keyframes fade-in-icon-bottom-right { + from { + opacity: 0; + transform: translate(0, 0); + } + to { + opacity: 0.1; + transform: translate(4px, 4px); + } +} diff --git a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.html b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.html new file mode 100644 index 00000000000..16ae2127862 --- /dev/null +++ b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.html @@ -0,0 +1,102 @@ +<!doctype html> +<html dir="$i18n{textdirection}" lang="$i18n{language}"> + <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="import" href="signin_shared_css.html"> + <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> + <link rel="stylesheet" href="sync_confirmation.css"></link> + <style is="custom-style" include="signin-dialog-shared"> +<if expr="is_macosx or is_linux"> + #undoButton { + -webkit-margin-end: 8px; + -webkit-margin-start: 0; + } +</if> + </style> + </head> + <body> + <div class="container"> + <div class="top-title-bar">$i18n{syncConfirmationTitle}</div> + <div class="details" id="syncConfirmationDetails"> + <div id="picture-container"> + <div id="illustration"> + <div id="icons"> + <div id="icon-bookmarks" class="fade-top-left"></div> + <div id="icon-extensions" class="fade-top-left"></div> + <div id="icon-passwords" class="fade-bottom-left"></div> + <div id="icon-history" class="fade-top-right"></div> + <div id="icon-tabs" class="fade-middle-right"></div> + <div id="icon-themes" class="fade-bottom-right"></div> + <div id="icon-circle-open" class="fade-middle-left"></div> + <div id="icon-circle-1" class="icon-circle fade-middle-left"> + </div> + <div id="icon-circle-2" class="icon-circle fade-top-right"></div> + <div id="icon-circle-3" class="icon-circle fade-middle-right"> + </div> + <div id="icon-circle-4" class="icon-circle fade-top-right"></div> + </div> + <div class="picture"> + <img id="profile-picture"> + <div id="checkmark-circle"> + <svg id="checkmark-check" width="13" height="10" + viewBox="0 0 13 10"> + <path id="checkmark-path" d="M1 5l3.5 3.5L12 1" stroke="#FFF" + stroke-width="2" stroke-dasharray="16" + stroke-dashoffset="16" fill="none"></path> + </svg> + </div> + </div> + </div> + </div> + <div class="message-container"> + <!-- + "Chrome sync" is the Google Cloud Based services used for sync. Thus + this section uses the Chrome logo even for Chromium builds. + --> + <div id="chrome-logo" class="logo"></div> + <div> + <div class="title">$i18n{syncConfirmationChromeSyncTitle}</div> + <div class="body text">$i18n{syncConfirmationChromeSyncBody}</div> + </div> + </div> + <div class="message-container"> + <!-- + This section uses the Google logo even for Chromium builds as the + user can personalize their Google services from this screen. + --> + <div id="googleg-logo" class="logo"></div> + <div> + <div class="title"> + $i18n{syncConfirmationPersonalizeServicesTitle} + </div> + <div class="body text"> + $i18n{syncConfirmationPersonalizeServicesBody} + </div> + </div> + </div> + <div class="message-container"> + <div class="body">$i18nRaw{syncConfirmationSyncSettingsLinkBody}</div> + </div> + </div> + <div class="details" id="syncDisabledDetails"> + <div class="body text">$i18n{syncDisabledConfirmationDetails}</div> + </div> + <div class="action-container"> + <paper-button class="primary-action" id="confirmButton"> + $i18n{syncConfirmationConfirmLabel} + </paper-button> + <paper-button class="secondary-action" id="undoButton"> + $i18n{syncConfirmationUndoLabel} + </paper-button> + </div> + </div> + </body> + <script src="chrome://resources/js/cr.js"></script> + <script src="chrome://resources/js/load_time_data.js"></script> + <script src="chrome://resources/js/util.js"></script> + <script src="sync_confirmation.js"></script> + <script src="chrome://sync-confirmation/strings.js"></script> +</html> diff --git a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.js b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.js new file mode 100644 index 00000000000..315f22e296a --- /dev/null +++ b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.js @@ -0,0 +1,72 @@ +/* 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('sync.confirmation', function() { + 'use strict'; + + function onConfirm(e) { + chrome.send('confirm'); + } + + function onUndo(e) { + chrome.send('undo'); + } + + function onGoToSettings(e) { + chrome.send('goToSettings'); + } + + function initialize() { + document.addEventListener('keydown', onKeyDown); + $('confirmButton').addEventListener('click', onConfirm); + $('undoButton').addEventListener('click', onUndo); + if (loadTimeData.getBoolean('isSyncAllowed')) { + $('settingsLink').addEventListener('click', onGoToSettings); + $('profile-picture').addEventListener('load', onPictureLoaded); + $('syncDisabledDetails').hidden = true; + } else { + $('syncConfirmationDetails').hidden = true; + } + + // Prefer using |document.body.offsetHeight| instead of + // |document.body.scrollHeight| as it returns the correct height of the + // even when the page zoom in Chrome is different than 100%. + chrome.send('initializedWithSize', [document.body.offsetHeight]); + } + + function clearFocus() { + document.activeElement.blur(); + } + + function setUserImageURL(url) { + if (loadTimeData.getBoolean('isSyncAllowed')) { + $('profile-picture').src = url; + } + } + + function onPictureLoaded(e) { + if (loadTimeData.getBoolean('isSyncAllowed')) { + $('picture-container').classList.add('loaded'); + } + } + + function onKeyDown(e) { + // If the currently focused element isn't something that performs an action + // on "enter" being pressed and the user hits "enter", perform the default + // action of the dialog, which is "OK, Got It". + if (e.key == 'Enter' && + !/^(A|PAPER-(BUTTON|CHECKBOX))$/.test(document.activeElement.tagName)) { + $('confirmButton').click(); + e.preventDefault(); + } + } + + return { + clearFocus: clearFocus, + initialize: initialize, + setUserImageURL: setUserImageURL + }; +}); + +document.addEventListener('DOMContentLoaded', sync.confirmation.initialize); diff --git a/chromium/chrome/browser/resources/snippets_internals.html b/chromium/chrome/browser/resources/snippets_internals.html index 07898877d79..27ee59e3a95 100644 --- a/chromium/chrome/browser/resources/snippets_internals.html +++ b/chromium/chrome/browser/resources/snippets_internals.html @@ -99,6 +99,11 @@ found in the LICENSE file. <div> <button id="debug-log-dump" type="button">Dump the debug log</button> </div> + <div> + <button type="button" class="submit-clear-cached-suggestions"> + Clear cached suggestions + </button> + </div> </div> </div> @@ -150,10 +155,6 @@ found in the LICENSE file. </div> <div class="vertical-buttons"> <button jsvalues="category-id:categoryId" type="button" - class="submit-clear-cached-suggestions"> - Clear cached suggestions - </button> - <button jsvalues="category-id:categoryId" type="button" class="toggle-dismissed-suggestions"> Show dismissed suggestions </button> diff --git a/chromium/chrome/browser/resources/snippets_internals.js b/chromium/chrome/browser/resources/snippets_internals.js index 63f19c5267c..eb316f77664 100644 --- a/chromium/chrome/browser/resources/snippets_internals.js +++ b/chromium/chrome/browser/resources/snippets_internals.js @@ -111,8 +111,7 @@ cr.define('chrome.SnippetsInternals', function() { function onClearCachedButtonClicked(event) { event.preventDefault(); - var id = parseInt(event.currentTarget.getAttribute('category-id'), 10); - chrome.send('clearCachedSuggestions', [id]); + chrome.send('clearCachedSuggestions'); } function onClearDismissedButtonClicked(event) { diff --git a/chromium/chrome/browser/resources/task_scheduler_internals/resources.grd b/chromium/chrome/browser/resources/task_scheduler_internals/resources.grd index 08419ac7291..7fee6ca5144 100644 --- a/chromium/chrome/browser/resources/task_scheduler_internals/resources.grd +++ b/chromium/chrome/browser/resources/task_scheduler_internals/resources.grd @@ -14,13 +14,16 @@ <includes> <include name="IDR_TASK_SCHEDULER_INTERNALS_RESOURCES_INDEX_HTML" file="index.html" - type="BINDATA" /> + type="BINDATA" + compress="gzip" /> <include name="IDR_TASK_SCHEDULER_INTERNALS_RESOURCES_INDEX_CSS" file="index.css" - type="BINDATA" /> + type="BINDATA" + compress="gzip" /> <include name="IDR_TASK_SCHEDULER_INTERNALS_RESOURCES_INDEX_JS" file="index.js" - type="BINDATA" /> + type="BINDATA" + compress="gzip" /> </includes> </release> </grit> diff --git a/chromium/chrome/browser/resources/unpack_pak.py b/chromium/chrome/browser/resources/unpack_pak.py index 8eda2e871fb..cf77483dfad 100755 --- a/chromium/chrome/browser/resources/unpack_pak.py +++ b/chromium/chrome/browser/resources/unpack_pak.py @@ -25,7 +25,7 @@ def unpack(pak_path, out_path): pak_dir = os.path.dirname(pak_path) pak_id = os.path.splitext(os.path.basename(pak_path))[0] - data = data_pack.DataPack.ReadDataPack(pak_path) + data = data_pack.ReadDataPack(pak_path) # Associate numerical grit IDs to strings. # For example 120045 -> 'IDR_SETTINGS_ABOUT_PAGE_HTML' diff --git a/chromium/chrome/browser/resources/welcome/default.webp b/chromium/chrome/browser/resources/welcome/default.webp Binary files differnew file mode 100644 index 00000000000..eb75d055893 --- /dev/null +++ b/chromium/chrome/browser/resources/welcome/default.webp diff --git a/chromium/chrome/browser/resources/welcome/dice_welcome/compiled_resources2.gyp b/chromium/chrome/browser/resources/welcome/dice_welcome/compiled_resources2.gyp new file mode 100644 index 00000000000..9b34e91a70b --- /dev/null +++ b/chromium/chrome/browser/resources/welcome/dice_welcome/compiled_resources2.gyp @@ -0,0 +1,24 @@ +# 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': 'welcome_app', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(EXTERNS_GYP):web_animations', + 'welcome_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'welcome_browser_proxy', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(EXTERNS_GYP):chrome_send', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +} diff --git a/chromium/chrome/browser/resources/welcome/dice_welcome/welcome.css b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome.css new file mode 100644 index 00000000000..07febed0c40 --- /dev/null +++ b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome.css @@ -0,0 +1,33 @@ +/* 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. */ + +body { + align-items: center; + color: var(--google-grey-700); + display: flex; + flex-direction: column; + font-size: 100%; + justify-content: center; + margin: 0; + min-height: 100vh; + text-align: center; +} + +.watermark { + -webkit-mask-image: url(chrome://resources/images/google_logo.svg); + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: 100%; + animation: fadeIn 1s cubic-bezier(0, 0, .2, 1) both; + background: var(--paper-grey-400); + bottom: 24px; + height: 24px; + position: absolute; + width: 74px; +} + +@media(max-height: 608px) { + .watermark { + display: none; + } +} diff --git a/chromium/chrome/browser/resources/welcome/dice_welcome/welcome.html b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome.html new file mode 100644 index 00000000000..f62bf685ccd --- /dev/null +++ b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome.html @@ -0,0 +1,18 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>$i18n{headerText}</title> + + <link rel="import" href="chrome://resources/html/polymer.html"> + <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> + <link rel="import" href="welcome_app.html"> + + <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> + <link rel="stylesheet" href="chrome://welcome/welcome.css"> + </head> + <body> + <welcome-app></welcome-app> + <div class="watermark"></div> + </body> +</html> diff --git a/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_app.html b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_app.html new file mode 100644 index 00000000000..1ecd003b38f --- /dev/null +++ b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_app.html @@ -0,0 +1,205 @@ +<link rel="import" href="chrome://resources/html/polymer.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/cr.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="import" href="welcome_browser_proxy.html"> + +<dom-module id="welcome-app"> + <template> + <style include="action-link"> + @keyframes slideUpContent { + from { + transform: translateY(120px); + } + } + + @keyframes fadeIn { + from { + opacity: 0; + } + } + + @keyframes fadeInAndSlideUp { + from { + opacity: 0; + transform: translateY(8px); + } + } + + @keyframes fadeOutAndSlideUp { + to { + height: 0; + opacity: 0; + transform: translateY(-8px); + } + } + + @keyframes spin { + from { + transform: rotate(1440deg) scale(0.8); + } + } + + @keyframes colorize { + from { + filter: grayscale(100%) brightness(128%) contrast(20%) brightness(161%); + opacity: .6; + } + } + + @keyframes bounce { + 0% { + transform: matrix3d(0.8, 0, 0, 0, 0, 0.8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + 7.61% { + transform: matrix3d(0.907, 0, 0, 0, 0, 0.907, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + 11.41% { + transform: matrix3d(0.948, 0, 0, 0, 0, 0.948, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + 15.12% { + transform: matrix3d(0.976, 0, 0, 0, 0, 0.976, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + 18.92% { + transform: matrix3d(0.996, 0, 0, 0, 0, 0.996, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + 22.72% { + transform: matrix3d(1.008, 0, 0, 0, 0, 1.008, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + 30.23% { + transform: matrix3d(1.014, 0, 0, 0, 0, 1.014, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + 50.25% { + transform: matrix3d(1.003, 0, 0, 0, 0, 1.003, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + 70.27% { + transform: matrix3d(0.999, 0, 0, 0, 0, 0.999, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + 100% { + transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + } + + .slider { + align-items: center; + animation: slideUpContent 600ms 3s cubic-bezier(.4, .2, 0, 1) both; + display: flex; + flex: 1; + flex-direction: column; + justify-content: center; + max-width: 500px; + } + + .heading-container { + animation: fadeInAndSlideUp 1s 600ms cubic-bezier(.4, .2, 0, 1) both; + color: var(--paper-grey-800); + font-size: 2.5em; + line-height: 1em; + margin-bottom: 0.25em; + margin-top: 1.5em; + position: relative; + } + + .heading { + animation: fadeOutAndSlideUp 600ms 2.6s cubic-bezier(.4, .2, 0, 1) forwards; + /* Makes sure fading-in/out doesn't impact the logo position. */ + position: absolute; + } + + .second-heading { + animation: fadeInAndSlideUp 600ms 3s cubic-bezier(.4, .2, 0, 1) both; + font-size: 0.6em; + } + + #logoWrapper { + animation: fadeIn 600ms both, bounce 1s 600ms linear both; + height: 96px; + position: relative; + width: 96px; + } + + #logo { + animation: spin 2.4s cubic-bezier(.4, .2, 0, 1) both, + colorize 300ms 700ms linear both; + background-image: -webkit-image-set(url(chrome://welcome/logo.png) 1x, + url(chrome://welcome/logo2x.png) 2x); + background-size: 100%; + height: 96px; + width: 96px; + } + + .signin { + animation: fadeInAndSlideUp 600ms 3s cubic-bezier(.4, .2, 0, 1) both; + } + + .signin-description { + font-size: 1em; + line-height: 1.725em; + max-width: 344px; + } + + .signin-buttons { + align-items: center; + display: flex; + flex-direction: row; + font-size: .8125em; + font-weight: 500; + justify-content: space-between; + margin-top: 2.5em; + text-transform: uppercase; + } + + #acceptButton { + -webkit-font-smoothing: antialiased; + background: var(--google-blue-500); + border-radius: 2px; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .1); + color: white; + line-height: 2.25rem; + padding: 0 2em; + transition: 300ms cubic-bezier(.4, .2, 0, 1); + will-change: box-shadow; + } + + #acceptButton:hover { + background: var(--paper-blue-a400); + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .1), 0 1px 2px rgba(0, 0, 0, .24); + } + + #acceptButton.keyboard-focus { + background: var(--google-blue-700); + } + + #declineButton { + color: var(--paper-grey-800); + display: inline-block; + margin: 1.5em; + text-decoration: none; + } + </style> + <div class="slider"> + <div id="logoWrapper"> + <div id="logo" on-tap="onLogoTap_"></div> + </div> + <div class="heading-container"> + <div class="heading">$i18n{headerText}</div> + <div class="second-heading">$i18n{secondHeaderText}</div> + </div> + <div class="signin"> + <div class="signin-description">$i18n{descriptionText}</div> + <div class="signin-buttons"> + <a is="action-link" id="declineButton" on-tap="onDecline_"> + $i18n{declineText} + </a> + <paper-button id="acceptButton" on-tap="onAccept_"> + $i18n{acceptText} + </paper-button> + </div> + </div> + </div> + </template> + <script src="welcome_app.js"></script> +</dom-module>
\ No newline at end of file diff --git a/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_app.js b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_app.js new file mode 100644 index 00000000000..8b663ea5f91 --- /dev/null +++ b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_app.js @@ -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. + +Polymer({ + is: 'welcome-app', + + welcomeBrowserProxy_: null, + + /** @override */ + ready: function() { + this.welcomeBrowserProxy_ = welcome.WelcomeBrowserProxyImpl.getInstance(); + }, + + /** @private */ + onAccept_: function() { + this.welcomeBrowserProxy_.handleActivateSignIn(); + }, + + /** @private */ + onDecline_: function() { + this.welcomeBrowserProxy_.handleUserDecline(); + }, + + /** @private */ + onLogoTap_: function() { + this.$.logo.animate( + { + transform: ['none', 'rotate(-10turn)'], + }, + /** @type {!KeyframeEffectOptions} */ ({ + duration: 500, + easing: 'cubic-bezier(1, 0, 0, 1)', + })); + }, +}); diff --git a/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_browser_proxy.html b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_browser_proxy.html new file mode 100644 index 00000000000..24f332053f2 --- /dev/null +++ b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_browser_proxy.html @@ -0,0 +1,2 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="welcome_browser_proxy.js"></script> diff --git a/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_browser_proxy.js b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_browser_proxy.js new file mode 100644 index 00000000000..9511779ccfb --- /dev/null +++ b/chromium/chrome/browser/resources/welcome/dice_welcome/welcome_browser_proxy.js @@ -0,0 +1,37 @@ +// 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 by the welcome page to interact with + * the browser. + */ + +cr.define('welcome', function() { + + /** @interface */ + class WelcomeBrowserProxy { + handleActivateSignIn() {} + handleUserDecline() {} + } + + /** @implements {welcome.WelcomeBrowserProxy} */ + class WelcomeBrowserProxyImpl { + /** @override */ + handleActivateSignIn() { + chrome.send('handleActivateSignIn'); + } + + /** @override */ + handleUserDecline() { + chrome.send('handleUserDecline'); + } + } + + cr.addSingletonGetter(WelcomeBrowserProxyImpl); + + return { + WelcomeBrowserProxy: WelcomeBrowserProxy, + WelcomeBrowserProxyImpl: WelcomeBrowserProxyImpl, + }; +}); diff --git a/chromium/chrome/browser/resources/welcome/win10/pin-small.webp b/chromium/chrome/browser/resources/welcome/pin.webp Binary files differindex a5281755428..a5281755428 100644 --- a/chromium/chrome/browser/resources/welcome/win10/pin-small.webp +++ b/chromium/chrome/browser/resources/welcome/pin.webp diff --git a/chromium/chrome/browser/resources/welcome/welcome.css b/chromium/chrome/browser/resources/welcome/welcome.css index 78c743be800..bf81b228580 100644 --- a/chromium/chrome/browser/resources/welcome/welcome.css +++ b/chromium/chrome/browser/resources/welcome/welcome.css @@ -215,7 +215,7 @@ body { } .watermark { - -webkit-mask-image: url(chrome://welcome/watermark.svg); + -webkit-mask-image: url(chrome://resources/images/google_logo.svg); -webkit-mask-repeat: no-repeat; -webkit-mask-size: 100%; animation: fadeIn 1s cubic-bezier(0, 0, .2, 1) both; diff --git a/chromium/chrome/browser/resources/welcome/win10/inline.css b/chromium/chrome/browser/resources/welcome/welcome_win10.css index 13e1025a420..13e1025a420 100644 --- a/chromium/chrome/browser/resources/welcome/win10/inline.css +++ b/chromium/chrome/browser/resources/welcome/welcome_win10.css diff --git a/chromium/chrome/browser/resources/welcome/win10/inline.html b/chromium/chrome/browser/resources/welcome/welcome_win10.html index fa5e6acc374..1689bdfbe12 100644 --- a/chromium/chrome/browser/resources/welcome/win10/inline.html +++ b/chromium/chrome/browser/resources/welcome/welcome_win10.html @@ -16,12 +16,13 @@ <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="stylesheet" href="/welcome.css"> + <link rel="stylesheet" href="/welcome_win10.css"> - <dom-module id="welcome-win10-inline"> + <dom-module id="welcome-win10"> <template> <style include="action-link"> :host { + --expandable-section-height: 28.375em; align-items: flex-start; display: inline-flex; flex-direction: column; @@ -119,7 +120,7 @@ } .section.expandable .section-steps { - height: 26.375em; + height: var(--expandable-section-height); max-height: 0; opacity: 0; transition: max-height 300ms cubic-bezier(.4, .2, 0, 1) 50ms, @@ -128,7 +129,7 @@ } .section.expandable.expanded .section-steps { - max-height: 26.375em; + max-height: var(--expandable-section-height); opacity: 1; transition: max-height 300ms cubic-bezier(.4, .2, 0, 1) 50ms, opacity 150ms 250ms; @@ -219,16 +220,6 @@ cursor: default; } - #browser-overlay { - left: 41%; - top: 81%; - } - - #edge-overlay { - left: 49%; - top: 88%; - } - #taskbar-overlay { left: 31%; top: 73%; @@ -240,6 +231,16 @@ font-weight: 500; } + #switch-anyway-overlay { + left: 38%; + top: 83%; + } + + #switch-anyway-overlay div { + color: rgb(0, 117, 218); + font-family: Segoe UI; + } + #icon-overlay { background-image: url(chrome://welcome-win10/logo-small.png); background-size: cover; @@ -252,32 +253,24 @@ /* These values are precisely set so that the text over the screenshot * starts scaling at the same time the image starts scaling too. */ @media (max-width: 626px) { - #browser-overlay { - font-size: 1.28vw; - } - - #edge-overlay { - font-size: 1.44vw; - } - #taskbar-overlay { font-size: 1.95vw; } + + #switch-anyway-overlay { + font-size: 1.92vw; + } } /* Font-sizes used when the screenshot exactly reaches its max size. */ @media (min-width: 626px) { - #browser-overlay { - font-size: 8px; - } - - #edge-overlay { - font-size: 9px; - } - #taskbar-overlay { font-size: 12.2px; } + + #switch-anyway-overlay { + font-size: 12px; + } } </style> <div class="header-logo" role="presentation"></div> @@ -311,21 +304,23 @@ $i18n{openSettingsText} </a> </li> + <template is="dom-if" if="[[!isAccelerated]]"> + <li> + <div role="presentation">$i18nRaw{clickEdgeText}</div> + </li> + </template> + <li> + <div role="presentation">$i18nRaw{clickSelectChrome}</div> + </li> <li> - <div role="presentation">$i18nRaw{clickEdgeText}</div> + <div role="presentation">$i18nRaw{clickSwitchAnywayText}</div> <div class="screenshot-image" id="default-image" role="presentation"> - <div class="screenshot-overlay" id="browser-overlay"> - <div aria-hidden="true">$i18n{webBrowserLabel}</div> - </div> - <div class="screenshot-overlay" id="edge-overlay"> - <div aria-hidden="true">$i18n{microsoftEdgeLabel}</div> + <div class="screenshot-overlay" id="switch-anyway-overlay"> + <div aria-hidden="true">$i18n{switchAnywayLabel}</div> </div> </div> </li> - <li> - <div role="presentation">$i18nRaw{clickSelectChrome}</div> - </li> </ol> </div> <template is="dom-if" if="[[isCombined]]"> @@ -363,10 +358,10 @@ $i18n{continueText} </paper-button> </template> - <script src="/welcome.js"></script> + <script src="/welcome_win10.js"></script> </dom-module> </head> <body> - <welcome-win10-inline></welcome-win10-inline> + <welcome-win10></welcome-win10> </body> </html> diff --git a/chromium/chrome/browser/resources/welcome/win10/inline.js b/chromium/chrome/browser/resources/welcome/welcome_win10.js index df67df5afb2..a961634a306 100644 --- a/chromium/chrome/browser/resources/welcome/win10/inline.js +++ b/chromium/chrome/browser/resources/welcome/welcome_win10.js @@ -3,23 +3,25 @@ // found in the LICENSE file. Polymer({ - is: 'welcome-win10-inline', + is: 'welcome-win10', properties: { // Determines if the combined variant should be displayed. The combined // variant includes instructions on how to pin Chrome to the taskbar. - isCombined: Boolean + isCombined: Boolean, + + // Indicates if the accelerated flow is enabled. + isAccelerated: Boolean, }, receivePinnedState_: function(isPinnedToTaskbar) { // Allow overriding of the result via a query parameter. // TODO(pmonette): Remove these checks when they are no longer needed. - /** @const */ - var VARIANT_KEY = 'variant'; - var VariantTypeMap = {'defaultonly': false, 'combined': true}; - var params = new URLSearchParams(location.search.slice(1)); - if (params.has(VARIANT_KEY) && params.get(VARIANT_KEY) in VariantTypeMap) - this.isCombined = VariantTypeMap[params.get(VARIANT_KEY)]; + const VARIANT_KEY = 'variant'; + const VARIANT_TYPE_MAP = {'defaultonly': false, 'combined': true}; + var params = new URLSearchParams(location.search); + if (params.has(VARIANT_KEY) && params.get(VARIANT_KEY) in VARIANT_TYPE_MAP) + this.isCombined = VARIANT_TYPE_MAP[params.get(VARIANT_KEY)]; else this.isCombined = !isPinnedToTaskbar; @@ -29,6 +31,25 @@ Polymer({ ready: function() { this.isCombined = false; + this.isAccelerated = false; + + const FLOWTYPE_KEY = 'flowtype'; + const FLOW_TYPE_MAP = {'regular': false, 'accelerated': true}; + var params = new URLSearchParams(location.search); + if (params.has(FLOWTYPE_KEY)) { + if (params.get(FLOWTYPE_KEY) in FLOW_TYPE_MAP) { + this.isAccelerated = FLOW_TYPE_MAP[params.get(FLOWTYPE_KEY)]; + + // Adjust the height since the accelerated flow contains fewer steps. + this.customStyle['--expandable-section-height'] = '26.375em'; + this.updateStyles(); + } else { + console.log( + 'Found invalid value for the \'flowtype\' parameter: %s', + params.get(FLOWTYPE_KEY)); + } + } + // Asynchronously check if Chrome is pinned to the taskbar. cr.sendWithPromise('getPinnedToTaskbarState') .then(this.receivePinnedState_.bind(this)); diff --git a/chromium/chrome/browser/resources/welcome/win10/README.md b/chromium/chrome/browser/resources/welcome/win10/README.md deleted file mode 100644 index efaee2433ff..00000000000 --- a/chromium/chrome/browser/resources/welcome/win10/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Windows 10 Welcome page - -There are 2 different styles of the Windows 10 Welcome Page. A/B testing will -be done to find out which one of them is better. - -TODO(pmonette): Experiment with the 2 versions of the Welcome Page and -get rid of one of them. Bug id 658918. diff --git a/chromium/chrome/browser/resources/welcome/win10/default-large.webp b/chromium/chrome/browser/resources/welcome/win10/default-large.webp Binary files differdeleted file mode 100644 index cb55e122d12..00000000000 --- a/chromium/chrome/browser/resources/welcome/win10/default-large.webp +++ /dev/null diff --git a/chromium/chrome/browser/resources/welcome/win10/default-small.webp b/chromium/chrome/browser/resources/welcome/win10/default-small.webp Binary files differdeleted file mode 100644 index 9a1345ada51..00000000000 --- a/chromium/chrome/browser/resources/welcome/win10/default-small.webp +++ /dev/null diff --git a/chromium/chrome/browser/resources/welcome/win10/pin-large.webp b/chromium/chrome/browser/resources/welcome/win10/pin-large.webp Binary files differdeleted file mode 100644 index bf1779515cd..00000000000 --- a/chromium/chrome/browser/resources/welcome/win10/pin-large.webp +++ /dev/null diff --git a/chromium/chrome/browser/resources/welcome/win10/sectioned.css b/chromium/chrome/browser/resources/welcome/win10/sectioned.css deleted file mode 100644 index c37f446a782..00000000000 --- a/chromium/chrome/browser/resources/welcome/win10/sectioned.css +++ /dev/null @@ -1,323 +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. */ - -body { - box-sizing: border-box; - color: var(--paper-grey-900); - display: flex; - flex-direction: column; - font-size: 100%; - justify-content: center; - margin: 0; - min-height: 100vh; -} - -a { - color: var(--google-blue-500); - text-decoration: none; -} - -ol { - margin: 0; - padding: 0; -} - -strong { - font-weight: 500; -} - -.content { - padding: 64px 24px 24px 24px; -} - -.header-logo { - content: url(chrome://welcome-win10/logo-large.png); - height: 4em; -} - -.text { - margin: 0 auto; - max-width: 45em; -} - -.heading { - font-size: 2.125em; - margin-bottom: .25em; - margin-top: 1.5em; -} - -.subheading { - color: var(--google-grey-500); - font-size: 1em; - margin-bottom: 1.5em; - margin-top: .25em; - text-align: center; -} - -.sections { - margin-bottom: 2em; -} - -.section.expandable { - border-top: 1px solid var(--google-grey-300); -} - -.section.expandable:last-child { - border-bottom: 1px solid var(--google-grey-300); -} - -.section.expandable .section-heading { - color: var(--google-blue-500); - cursor: pointer; -} - -.section-heading { - align-items: center; - display: flex; - padding: 1.5em 0; -} - -.section-heading-text { - flex: 1; - font-weight: 500; -} - -.section.expandable .section-heading-text { - font-weight: normal; -} - -.section.expandable.expanded .section-heading-text { - font-weight: 500; -} - -.section-heading-expand { - height: 1.25em; - opacity: 0.54; - transition: transform 150ms cubic-bezier(.4, .2, 0, 1) 50ms; - width: 1.25em; -} - -.section.expandable.expanded .section-heading-expand { - transform: rotate(180deg); - transition-delay: 150ms; -} - -.section-steps { - overflow: hidden; -} - -.section-steps li { - -webkit-margin-start: 1.25em; - -webkit-padding-start: 1em; - margin-bottom: 1em; -} - -.section-steps li:last-child { - margin-bottom: 1em; -} - -.section.expandable .section-steps { - max-height: 0; - opacity: 0; - transition: max-height 300ms cubic-bezier(.4, .2, 0, 1) 50ms, opacity 150ms; -} - -.section.expandable.expanded .section-steps { - max-height: 8.75em; - opacity: 1; - transition: max-height 300ms cubic-bezier(.4, .2, 0, 1) 50ms, - opacity 150ms 250ms; -} - -.button { - -webkit-font-smoothing: antialiased; - background: var(--google-blue-500); - border-radius: 2px; - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .1); - color: #fff; - display: inline-block; - font-size: .8125em; - font-weight: 500; - line-height: 2.25rem; - padding: 0 1em; - text-align: center; - transition: 300ms cubic-bezier(.4, .2, 0, 1); - will-change: box-shadow; -} - -.button:hover { - background: var(--paper-blue-a400); - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .1), 0 1px 2px rgba(0, 0, 0, .24); -} - -.bg { - background: var(--paper-light-blue-700); - flex: 1; - margin-top: 96px; - padding: 24px; -} - -.logo-small { - content: url(chrome://welcome-win10/logo-small.png); - display: inline; - height: 1.25em; - vertical-align: top; - width: 1.25em; -} - -.screenshots { - display: block; - height: 440px; - margin: 0 auto; - max-width: 100%; - position: relative; - top: -96px; - width: 720px; -} - -.screenshot-image { - box-shadow: 0 0 8px rgba(0, 0, 0, .12), 0 4px 16px rgba(0, 0, 0, .24); - height: 56vw; - max-height: 440px; - max-width: 720px; - min-height: 294px; - min-width: 480px; - position: absolute; - transition: opacity 150ms; - width: 92vw; -} - -.screenshot-image.hidden { - opacity: 0; -} - -#default-image { - background: url(chrome://welcome-win10/default.webp); - background-repeat: no-repeat; - background-size: cover; -} - -#taskbar-image { - background: url(chrome://welcome-win10/pin.webp); - background-repeat: no-repeat; - background-size: cover; -} - -.screenshot-overlay { - box-sizing: border-box; - line-height: 0; - position: absolute; -} - -#browser-overlay { - left: 42.5%; - top: 76%; -} - -#edge-overlay { - left: 50%; - top: 84%; -} - -#taskbar-overlay { - left: 62.2%; - top: 81.5%; -} - -#taskbar-overlay div { - color: #ccc; - font-family: Tahoma, Verdana, Segoe, sans-serif; - font-weight: 500; -} - -#icon-overlay { - background-image: url(chrome://welcome-win10/logo-small.png); - background-size: cover; - height: 5.8%; - left: 70.60%; - top: 93.1%; - width: 3.5%; -} - - -/* These values are precisely set so that the text over the screenshot starts - * scaling at the same time the image starts scaling too. */ -@media (min-width: 520px) { - #browser-overlay { - font-size: 1.8vw; - } - - #edge-overlay { - font-size: 2.05vw; - } - - #taskbar-overlay { - font-size: 1.35vw; - } -} - -/* Font-sizes used when the screenshot exactly reaches its max size. */ -@media (min-width: 780px) { - #browser-overlay { - font-size: 14px; - } - - #edge-overlay { - font-size: 16px; - } - - #taskbar-overlay { - font-size: 10.5px; - } -} - -/* Font-sizes used when the screenshot exactly reaches its min size. */ -@media(max-width: 520px) { - #browser-overlay { - font-size: 9px; - } - - #edge-overlay { - font-size: 10.5px; - } - - #taskbar-overlay { - font-size: 7px; - } -} - -@media (min-width: 1280px) { - body { - flex-direction: row; - } - - .content { - align-items: center; - display: flex; - flex: 1; - justify-content: flex-end; - padding: 96px; - } - - .text { - margin: 0 180px; - max-width: none; - width: 400px; - } - - .bg { - align-items: center; - display: flex; - flex: 1; - margin: 0; - max-width: 42%; - padding: 0; - } - - .screenshots { - margin-left: -180px; - max-width: none; - top: 0; - } -} diff --git a/chromium/chrome/browser/resources/welcome/win10/sectioned.html b/chromium/chrome/browser/resources/welcome/win10/sectioned.html deleted file mode 100644 index 13b7ff6b059..00000000000 --- a/chromium/chrome/browser/resources/welcome/win10/sectioned.html +++ /dev/null @@ -1,90 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <meta charset="utf-8"> - <title>$i18n{headerText}</title> - - <link rel="import" href="chrome://resources/html/polymer.html"> - - <link rel="import" href="chrome://resources/cr_elements/icons.html"> - <link rel="import" href="chrome://resources/html/cr.html"> - <link rel="import" href="chrome://resources/html/util.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"> - - <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> - <link rel="stylesheet" href="/welcome.css"> - - <script src="/welcome.js"></script> -</head> -<body> - <template is="dom-bind" id="sectioned-app"> - <div class="content"> - <div class="text"> - <div class="header-logo"></div> - <div class="heading">$i18n{headerText}</div> - <div class="sections"> - <div class$="[[computeClasses(isCombined)]]"> - <div class="section-heading" on-tap="onToggle"> - <div class="section-heading-text"> - $i18n{defaultBrowserSubheaderText} - </div> - <template is="dom-if" if="[[isCombined]]"> - <iron-icon class="section-heading-expand" icon="cr:expand-more"> - </iron-icon> - </template> - </div> - <ol class="section-steps"> - <li> - <a href="#" on-tap="onOpenSettings">$i18n{openSettingsText}</a> - </li> - <li>$i18nRaw{clickEdgeText}</li> - <li>$i18nRaw{clickSelectChrome}</li> - </ol> - </div> - <template is="dom-if" if="[[isCombined]]"> - <div class="section expandable"> - <div class="section-heading" on-tap="onToggle"> - <div class="section-heading-text">$i18n{pinSubheaderText}</div> - <iron-icon class="section-heading-expand" icon="cr:expand-more"> - </iron-icon> - </div> - <ol class="section-steps"> - <li>$i18nRaw{rightClickText}</li> - <li>$i18nRaw{pinInstructionText}</li> - </ol> - </div> - </template> - </div> - <paper-button class="button" on-tap="onContinue"> - $i18n{continueText} - </paper-button> - </div> - </div> - <div class="bg"> - <div class="screenshots"> - <div class="screenshot-image" id="default-image"> - <div class="screenshot-overlay" - id="browser-overlay"> - <div>$i18n{webBrowserLabel}</div> - </div> - <div class="screenshot-overlay" - id="edge-overlay"> - <div>$i18n{microsoftEdgeLabel}</div> - </div> - </div> - <div class="screenshot-image hidden" id="taskbar-image"> - <div class="screenshot-overlay" - id="taskbar-overlay"> - <div>$i18n{pinToTaskbarLabel}</div> - </div> - <div class="screenshot-overlay" - id="icon-overlay"> - </div> - </div> - </div> - </div> - </template> -</body> -</html> diff --git a/chromium/chrome/browser/resources/welcome/win10/sectioned.js b/chromium/chrome/browser/resources/welcome/win10/sectioned.js deleted file mode 100644 index 09137e957f6..00000000000 --- a/chromium/chrome/browser/resources/welcome/win10/sectioned.js +++ /dev/null @@ -1,74 +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.define('sectioned', function() { - 'use strict'; - - function computeClasses(isCombined) { - if (isCombined) - return 'section expandable expanded'; - return 'section'; - } - - function onContinue() { - chrome.send('handleContinue'); - } - - function onOpenSettings() { - chrome.send('handleSetDefaultBrowser'); - } - - function onToggle(app) { - if (app.isCombined) { - // Toggle sections. - var sections = document.querySelectorAll('.section.expandable'); - sections.forEach(function(section) { - section.classList.toggle('expanded'); - }); - // Toggle screenshots. - var screenshots = document.querySelectorAll('.screenshot-image'); - screenshots.forEach(function(screenshot) { - screenshot.classList.toggle('hidden'); - }); - } - } - - function initialize() { - var app = $('sectioned-app'); - - // Set variables. - // Determines if the combined variant should be displayed. The combined - // variant includes instructions on how to pin Chrome to the taskbar. - app.isCombined = false; - - // Set handlers. - app.computeClasses = computeClasses; - app.onContinue = onContinue; - app.onOpenSettings = onOpenSettings; - app.onToggle = onToggle.bind(this, app); - - - // Asynchronously check if Chrome is pinned to the taskbar. - cr.sendWithPromise('getPinnedToTaskbarState') - .then(function(isPinnedToTaskbar) { - // Allow overriding of the result via a query parameter. - // TODO(pmonette): Remove these checks when they are no longer needed. - /** @const */ var VARIANT_KEY = 'variant'; - var VariantType = {DEFAULT_ONLY: 'defaultonly', COMBINED: 'combined'}; - var params = new URLSearchParams(location.search.slice(1)); - if (params.has(VARIANT_KEY)) { - if (params.get(VARIANT_KEY) === VariantType.DEFAULT_ONLY) - app.isCombined = false; - else if (params.get(VARIANT_KEY) === VariantType.COMBINED) - app.isCombined = true; - } else { - app.isCombined = !isPinnedToTaskbar; - } - }); - } - - return {initialize: initialize}; -}); - -document.addEventListener('DOMContentLoaded', sectioned.initialize); |