Commit 956b94d4 authored by rbpotter's avatar rbpotter Committed by Commit Bot

Print Preview Componentization: Destination dialog search highlighting

Add the highlighting of the search query in the destination change
dialog for Print Preview. Refactor some highlighting code out of
chrome/browser/resources/settings/search_settings.js in order to
reuse it in print_preview.

Bug: 773928
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: Id79fdb23717c83337cc5e208331066f364b355bf
Reviewed-on: https://chromium-review.googlesource.com/892209
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#533593}
parent 8d5c6d9b
......@@ -193,10 +193,19 @@
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
'../compiled_resources2.gyp:native_layer',
'../data/compiled_resources2.gyp:destination',
'destination_list_item',
'print_preview_search_box',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
'target_name': 'destination_list_item',
'dependencies': [
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:search_highlight_utils',
'../data/compiled_resources2.gyp:destination',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
'target_name': 'print_preview_search_box',
'dependencies': [
......
......@@ -5,6 +5,7 @@
<link rel="import" href="chrome://resources/html/i18n_behavior.html">
<link rel="import" href="../native_layer.html">
<link rel="import" href="../data/destination.html">
<link rel="import" href="destination_list_item.html">
<link rel="import" href="print_preview_shared_css.html">
<link rel="import" href="throbber_css.html">
......@@ -63,12 +64,6 @@
padding-top: 6px;
}
:host ul {
list-style-type: none;
margin: 0;
padding: 0;
}
:host .list-item {
-webkit-padding-end: 2px;
-webkit-padding-start: 18px;
......@@ -90,95 +85,6 @@
.list-item:focus {
outline: none;
}
.list-item.stale :-webkit-any(.destination-list-item-icon,
.destination-list-item-name,
.connection-status) {
opacity: 0.4;
}
.destination-list-item-icon {
-webkit-margin-end: 8px;
display: inline-block;
flex: 0 0 auto;
height: 24px;
transition: opacity 150ms;
vertical-align: middle;
width: 24px;
}
.destination-list-item-name,
.search-hint {
flex: 0 1 auto;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
}
.list-item .search-hint {
-webkit-margin-start: 1em;
color: #999;
font-size: 75%;
}
.list-item .connection-status,
.list-item .learn-more-link {
-webkit-margin-start: 1em;
flex: 0 0 auto;
font-size: 75%;
line-height: 24px;
vertical-align: middle;
}
.list-item .learn-more-link {
color: rgb(51, 103, 214);
}
.register-promo {
-webkit-margin-start: 1em;
flex: 0 0 auto;
}
.extension-controlled-indicator {
display: flex;
flex: 1;
justify-content: flex-end;
min-width: 150px;
}
.extension-name {
-webkit-margin-start: 1em;
color: #777;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.extension-icon {
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
flex: 0 0 auto;
height: 24px;
margin: 0 3px;
width: 24px;
}
.configuring-in-progress-text,
.configuring-failed-text {
-webkit-margin-start: 1em;
flex: 0 1 auto;
line-height: 24px;
vertical-align: middle;
}
.configuring-failed-text {
color: red;
font-style: italic;
}
</style>
<header>
<h4 class="title">[[title]]</h4>
......@@ -190,48 +96,12 @@
<div class="throbber"></div>
</div>
</header>
<ul>
<template is="dom-repeat" items="[[displayedDestinations_]]">
<li class$="list-item [[getStaleCssClass_(item.isOfflineOrInvalid)]]"
title="[[item.displayName]]"
hidden$="[[isDestinationHidden_(index, showAll_)]]"
on-tap="onDestinationSelected_">
<img class="destination-list-item-icon"
src="[[item.iconUrl]]" srcset="[[item.srcSet]]">
<span class="destination-list-item-name">[[item.displayName]]</span>
<span class="search-hint">[[getSearchHint_(item, searchQuery)]]</span>
<span class="connection-status"
hidden$="[[!item.isOfflineOrInvalid]]">
[[item.connectionStatusText]]
</span>
<a is="action-link" class="learn-more-link"
hidden$="[[!item.shouldShowInvalidCertificateError]]">
$i18n{learnMore}
</a>
<span class="register-promo" hidden$="[[!item.isUnregistered]]">
<button class="register-promo-button">
$i18n{registerPromoButtonText}
</button>
</span>
<span class="extension-controlled-indicator"
hidden$="[[!item.isExtension]]">
<span class="extension-name">[[item.extensionName]]</span>
<span class="extension-icon" role="button" tabindex="0"></span>
</span>
<if expr="chromeos">
<span class="configuring-in-progress-text" hidden>
$i18n{configuringInProgressText}
<span class="configuring-text-jumping-dots">
<span>.</span><span>.</span><span>.</span>
</span>
</span>
<span class="configuring-failed-text" hidden>
$i18n{configuringFailedText}
</span>
</if>
</li>
</template>
</ul>
<template is="dom-repeat" items="[[destinations]]" notify-dom-change
filter="[[computeFilter_(searchQuery, showAll_, destinations)]]">
<print-preview-destination-list-item class="list-item"
on-tap="onDestinationSelected_" destination="[[item]]">
</print-preview-destination-list-item>
</template>
<div class="no-destinations-message" hidden$="[[hasDestinations_]]">
$i18n{noDestinationsMessage}
</div>
......@@ -240,7 +110,7 @@
$i18n{showAllButtonText}
</button>
<span class="total">
[[i18n('destinationCount', displayedDestinations_.length)]]
[[i18n('destinationCount', matchingDestinationsCount_)]]
</span>
</footer>
</template>
......
......@@ -5,6 +5,8 @@
(function() {
'use strict';
// TODO (rbpotter): Adjust the short list size based on the dialog height
// instead of always using this constant.
/** @type {number} */
const SHORT_DESTINATION_LIST_SIZE = 4;
......@@ -41,88 +43,82 @@ Polymer({
value: false,
},
/** @private {!Array<!print_preview.Destination>} */
displayedDestinations_: {
type: Array,
computed: 'computeDisplayedDestinations_(destinations, searchQuery)',
/** @private {number} */
matchingDestinationsCount_: {
type: Number,
computed: 'computeMatchingDestinationsCount_(destinations, searchQuery)',
},
/** @type {boolean} */
footerHidden_: {
type: Boolean,
computed: 'computeFooterHidden_(displayedDestinations_, showAll_)',
computed: 'computeFooterHidden_(matchingDestinationsCount_, showAll_)',
},
/** @private {boolean} */
hasDestinations_: {
type: Boolean,
computed: 'computeHasDestinations_(displayedDestinations_)',
computed: 'computeHasDestinations_(matchingDestinationsCount_)',
},
},
/**
* @return {!Array<!print_preview.Destination>}
* @private
*/
computeDisplayedDestinations_: function() {
if (!this.searchQuery)
return assert(this.destinations);
return this.destinations.filter(destination => {
return destination.matches(assert(this.searchQuery));
});
listeners: {
'dom-change': 'updateListItems_',
},
/**
* @param {!print_preview.Destination} destination The destination to get the
* search hint for.
* @return {string} The property or properties matching the search query.
* @private
*/
getSearchHint_: function(destination) {
if (!this.searchQuery)
return '';
let hint = '';
destination.extraPropertiesToMatch.some(property => {
if (property.match(this.searchQuery))
hint += property;
});
return hint;
/** @private {number} */
shownCount_: 0,
/** @private */
updateListItems_: function() {
this.shadowRoot
.querySelectorAll('print-preview-destination-list-item:not([hidden])')
.forEach(item => item.update(this.searchQuery));
},
/**
* @return {boolean}
* @return {Function}
* @private
*/
computeFooterHidden_: function() {
return this.displayedDestinations_.length < SHORT_DESTINATION_LIST_SIZE ||
this.showAll_;
computeFilter_: function() {
this.shownCount_ = 0;
return destination => {
const isShown =
(!this.searchQuery || destination.matches(this.searchQuery)) &&
(this.shownCount_ < SHORT_DESTINATION_LIST_SIZE || this.showAll_);
if (isShown)
this.shownCount_++;
return isShown;
};
},
/**
* @return {boolean}
* @return {number}
* @private
*/
computeHasDestinations_: function() {
return this.displayedDestinations_.length > 0;
computeMatchingDestinationsCount_: function() {
return this.destinations
.filter(destination => {
return !this.searchQuery || destination.matches(this.searchQuery);
})
.length;
},
/**
* @param {number} index The index of the destination in the list.
* @return {boolean}
* @private
*/
isDestinationHidden_: function(index) {
return index >= SHORT_DESTINATION_LIST_SIZE && !this.showAll_;
computeFooterHidden_: function() {
return this.matchingDestinationsCount_ < SHORT_DESTINATION_LIST_SIZE ||
this.showAll_;
},
/**
* @param {boolean} offlineOrInvalid Whether the destination is offline or
* invalid
* @return {string} An empty string or 'stale'.
* @return {boolean}
* @private
*/
getStaleCssClass_: function(offlineOrInvalid) {
return offlineOrInvalid ? 'stale' : '';
computeHasDestinations_: function() {
return this.matchingDestinationsCount_ > 0;
},
/** @private */
......
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="chrome://resources/html/search_highlight_utils.html">
<link rel="import" href="../native_layer.html">
<link rel="import" href="../data/destination.html">
<link rel="import" href="print_preview_shared_css.html">
<dom-module id="print-preview-destination-list-item">
<template>
<style include="print-preview-shared action-link cr-hidden-style">
:host .icon {
-webkit-margin-end: 8px;
display: inline-block;
flex: 0 0 auto;
height: 24px;
transition: opacity 150ms;
vertical-align: middle;
width: 24px;
}
:host .name,
:host .search-hint {
flex: 0 1 auto;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
}
:host .search-hint {
-webkit-margin-start: 1em;
color: #999;
font-size: 75%;
}
:host .connection-status,
:host .learn-more-link {
-webkit-margin-start: 1em;
flex: 0 0 auto;
font-size: 75%;
line-height: 24px;
vertical-align: middle;
}
:host .learn-more-link {
color: rgb(51, 103, 214);
}
:host .register-promo {
-webkit-margin-start: 1em;
flex: 0 0 auto;
}
:host .extension-controlled-indicator {
display: flex;
flex: 1;
justify-content: flex-end;
min-width: 150px;
}
:host .extension-name {
-webkit-margin-start: 1em;
color: #777;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
:host .extension-icon {
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
flex: 0 0 auto;
height: 24px;
margin: 0 3px;
width: 24px;
}
:host .configuring-in-progress-text,
:host .configuring-failed-text {
-webkit-margin-start: 1em;
flex: 0 1 auto;
line-height: 24px;
vertical-align: middle;
}
:host .configuring-failed-text {
color: red;
font-style: italic;
}
:host([stale]) :-webkit-any(.icon, .name, .connection-status) {
opacity: 0.4;
}
</style>
<img class="icon" src="[[destination.iconUrl]]"
srcset="[[destination.srcSet]]">
<span class="name searchable">[[destination.displayName]]</span>
<span class="search-hint searchable">[[searchHint_]]</span>
<span class="connection-status"
hidden$="[[!destination.isOfflineOrInvalid]]">
[[destination.connectionStatusText]]
</span>
<a is="action-link" class="learn-more-link"
hidden$="[[!destination.shouldShowInvalidCertificateError]]">
$i18n{learnMore}
</a>
<span class="register-promo" hidden$="[[!destination.isUnregistered]]">
<button class="register-promo-button">
$i18n{registerPromoButtonText}
</button>
</span>
<span class="extension-controlled-indicator"
hidden$="[[!destination.isExtension]]">
<span class="extension-name searchable">
[[destination.extensionName]]
</span>
<span class="extension-icon" role="button" tabindex="0"></span>
</span>
<if expr="chromeos">
<span class="configuring-in-progress-text" hidden>
$i18n{configuringInProgressText}
<span class="configuring-text-jumping-dots">
<span>.</span><span>.</span><span>.</span>
</span>
</span>
<span class="configuring-failed-text" hidden>
$i18n{configuringFailedText}
</span>
</if>
</template>
<script src="destination_list_item.js"></script>
</dom-module>
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
Polymer({
is: 'print-preview-destination-list-item',
properties: {
/** @type {!print_preview.Destination} */
destination: Object,
/** @private {string} */
searchHint_: {
type: String,
notify: true,
},
/** @type {boolean} */
stale: {
type: Boolean,
notify: true,
reflectToAttribute: true,
},
},
observers: [
'onDestinationPropertiesChange_(' +
'destination.displayName, destination.isOfflineOrInvalid)',
],
/** @private {boolean} */
highlighted_: false,
/** @private */
onDestinationPropertiesChange_: function() {
this.title = this.destination.displayName;
this.stale = this.destination.isOfflineOrInvalid;
},
/** @param {?RegExp} searchQuery The search query to update for. */
update: function(searchQuery) {
this.updateSearchHint_(searchQuery);
this.updateHighlighting_(searchQuery);
},
/**
* @param {?RegExp} searchQuery The search query to update the hint for.
* @private
*/
updateSearchHint_: function(searchQuery) {
if (!searchQuery) {
this.searchHint_ = '';
return;
}
this.searchHint_ = this.destination.extraPropertiesToMatch
.filter(p => p.match(searchQuery))
.join(' ');
},
/**
* @param {?RegExp} searchQuery The search query to update the hint for.
* @private
*/
updateHighlighting_: function(searchQuery) {
if (this.highlighted_) {
cr.search_highlight_utils.findAndRemoveHighlights(this);
this.highlighted_ = false;
}
if (!searchQuery)
return;
this.shadowRoot.querySelectorAll('.searchable').forEach(element => {
element.childNodes.forEach(node => {
if (node.nodeType != Node.TEXT_NODE)
return;
const textContent = node.nodeValue.trim();
if (textContent.length == 0)
return;
if (searchQuery.test(textContent)) {
// Don't highlight <select> nodes, yellow rectangles can't be
// displayed within an <option>.
// TODO(rbpotter): solve issue below before adding advanced
// settings so that this function can be re-used.
// TODO(dpapad): highlight <select> controls with a search bubble
// instead.
if (node.parentNode.nodeName != 'OPTION') {
cr.search_highlight_utils.highlight(
node, textContent.split(searchQuery));
this.highlighted_ = true;
}
}
});
});
},
});
......@@ -36,7 +36,7 @@ Polymer({
onSearchChanged_: function(e) {
const safeQuery = e.detail.trim().replace(SANITIZE_REGEX, '\\$&');
this.searchQuery =
safeQuery.length > 0 ? new RegExp(`(${safeQuery})`, 'ig') : null;
safeQuery.length > 0 ? new RegExp(`(${safeQuery})`, 'i') : null;
},
});
})();
......@@ -226,11 +226,17 @@
type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_LIST_HTML"
file="new/destination_list.html"
type="chrome_html"
preprocess="true" />
type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_LIST_JS"
file="new/destination_list.js"
type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_LIST_ITEM_HTML"
file="new/destination_list_item.html"
type="chrome_html"
preprocess="true" />
<structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_LIST_ITEM_JS"
file="new/destination_list_item.js"
type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_NEW_PRINT_PREVIEW_SEARCH_BOX_HTML"
file="new/print_preview_search_box.html"
type="chrome_html"
......
......@@ -57,6 +57,7 @@
'target_name': 'search_settings',
'dependencies': [
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:search_highlight_utils',
],
'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
},
......
......@@ -17,15 +17,6 @@ cr.exportPath('settings');
settings.SearchResult;
cr.define('settings', function() {
/** @type {string} */
const WRAPPER_CSS_CLASS = 'search-highlight-wrapper';
/** @type {string} */
const ORIGINAL_CONTENT_CSS_CLASS = 'search-highlight-original-content';
/** @type {string} */
const HIT_CSS_CLASS = 'search-highlight-hit';
/** @type {string} */
const SEARCH_BUBBLE_CSS_CLASS = 'search-bubble';
......@@ -65,14 +56,7 @@ cr.define('settings', function() {
* @private
*/
function findAndRemoveHighlights_(node) {
const wrappers = node.querySelectorAll('* /deep/ .' + WRAPPER_CSS_CLASS);
for (let i = 0; i < wrappers.length; i++) {
const wrapper = wrappers[i];
const originalNode =
wrapper.querySelector('.' + ORIGINAL_CONTENT_CSS_CLASS);
wrapper.parentElement.replaceChild(originalNode.firstChild, wrapper);
}
cr.search_highlight_utils.findAndRemoveHighlights(node);
const searchBubbles =
node.querySelectorAll('* /deep/ .' + SEARCH_BUBBLE_CSS_CLASS);
......@@ -80,46 +64,6 @@ cr.define('settings', function() {
searchBubbles[j].remove();
}
/**
* Applies the highlight UI (yellow rectangle) around all matches in |node|.
* @param {!Node} node The text node to be highlighted. |node| ends up
* being removed from the DOM tree.
* @param {!Array<string>} tokens The string tokens after splitting on the
* relevant regExp. Even indices hold text that doesn't need highlighting,
* odd indices hold the text to be highlighted. For example:
* const r = new RegExp('(foo)', 'i');
* 'barfoobar foo bar'.split(r) => ['bar', 'foo', 'bar ', 'foo', ' bar']
* @private
*/
function highlight_(node, tokens) {
const wrapper = document.createElement('span');
wrapper.classList.add(WRAPPER_CSS_CLASS);
// Use existing node as placeholder to determine where to insert the
// replacement content.
node.parentNode.replaceChild(wrapper, node);
// Keep the existing node around for when the highlights are removed. The
// existing text node might be involved in data-binding and therefore should
// not be discarded.
const span = document.createElement('span');
span.classList.add(ORIGINAL_CONTENT_CSS_CLASS);
span.style.display = 'none';
span.appendChild(node);
wrapper.appendChild(span);
for (let i = 0; i < tokens.length; ++i) {
if (i % 2 == 0) {
wrapper.appendChild(document.createTextNode(tokens[i]));
} else {
const hitSpan = document.createElement('span');
hitSpan.classList.add(HIT_CSS_CLASS);
hitSpan.style.backgroundColor = '#ffeb3b'; // --var(--paper-yellow-500)
hitSpan.textContent = tokens[i];
wrapper.appendChild(hitSpan);
}
}
}
/**
* Traverses the entire DOM (including Shadow DOM), finds text nodes that
* match the given regular expression and applies the highlight UI. It also
......@@ -163,8 +107,10 @@ cr.define('settings', function() {
// displayed within an <option>.
// TODO(dpapad): highlight <select> controls with a search bubble
// instead.
if (node.parentNode.nodeName != 'OPTION')
highlight_(node, textContent.split(request.regExp));
if (node.parentNode.nodeName != 'OPTION') {
cr.search_highlight_utils.highlight(
node, textContent.split(request.regExp));
}
}
// Returning early since TEXT_NODE nodes never have children.
return;
......
......@@ -2,6 +2,7 @@
<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="chrome://resources/html/search_highlight_utils.html">
<link rel="import" href="chrome://resources/html/promise_resolver.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
......
......@@ -10,7 +10,9 @@ cr.define('settings_test', function() {
// Don't import script if already imported (happens in Vulcanized mode).
suiteSetup(function() {
if (!window.settings || !settings.getSearchManager) {
return PolymerTest.loadScript('chrome://settings/search_settings.js');
return PolymerTest.loadScript(
'chrome://resources/js/search_highlight_utils.js') &&
PolymerTest.loadScript('chrome://settings/search_settings.js');
}
});
......
<script src="chrome://resources/js/search_highlight_utils.js"></script>
......@@ -31,6 +31,13 @@
],
'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
'target_name': 'search_highlight_utils',
'dependencies': [
'cr',
],
'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
'target_name': 'icon',
'dependencies': [
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
cr.define('cr.search_highlight_utils', function() {
/** @type {string} */
const WRAPPER_CSS_CLASS = 'search-highlight-wrapper';
/** @type {string} */
const ORIGINAL_CONTENT_CSS_CLASS = 'search-highlight-original-content';
/** @type {string} */
const HIT_CSS_CLASS = 'search-highlight-hit';
/**
* Applies the highlight UI (yellow rectangle) around all matches in |node|.
* @param {!Node} node The text node to be highlighted. |node| ends up
* being hidden.
* @param {!Array<string>} tokens The string tokens after splitting on the
* relevant regExp. Even indices hold text that doesn't need highlighting,
* odd indices hold the text to be highlighted. For example:
* const r = new RegExp('(foo)', 'i');
* 'barfoobar foo bar'.split(r) => ['bar', 'foo', 'bar ', 'foo', ' bar']
*/
function highlight(node, tokens) {
const wrapper = document.createElement('span');
wrapper.classList.add(WRAPPER_CSS_CLASS);
// Use existing node as placeholder to determine where to insert the
// replacement content.
node.parentNode.replaceChild(wrapper, node);
// Keep the existing node around for when the highlights are removed. The
// existing text node might be involved in data-binding and therefore should
// not be discarded.
const span = document.createElement('span');
span.classList.add(ORIGINAL_CONTENT_CSS_CLASS);
span.style.display = 'none';
span.appendChild(node);
wrapper.appendChild(span);
for (let i = 0; i < tokens.length; ++i) {
if (i % 2 == 0) {
wrapper.appendChild(document.createTextNode(tokens[i]));
} else {
const hitSpan = document.createElement('span');
hitSpan.classList.add(HIT_CSS_CLASS);
hitSpan.style.backgroundColor = '#ffeb3b'; // --var(--paper-yellow-500)
hitSpan.textContent = tokens[i];
wrapper.appendChild(hitSpan);
}
}
}
/**
* Finds all previous highlighted nodes under |node| (both within self and
* children's Shadow DOM) and replaces the highlights (yellow rectangles)
* with the original search node.
* @param {!Node} node
* @private
*/
function findAndRemoveHighlights(node) {
const wrappers = node.querySelectorAll('* /deep/ .' + WRAPPER_CSS_CLASS);
for (let i = 0; i < wrappers.length; i++) {
const wrapper = wrappers[i];
const originalNode =
wrapper.querySelector('.' + ORIGINAL_CONTENT_CSS_CLASS);
wrapper.parentElement.replaceChild(originalNode.firstChild, wrapper);
}
}
return {
highlight: highlight,
findAndRemoveHighlights: findAndRemoveHighlights,
};
});
......@@ -369,6 +369,11 @@ without changes to the corresponding grd file. -->
<structure name="IDR_WEBUI_HTML_WEBUI_LISTENER_TRACKER"
file="html/webui_listener_tracker.html" type="chrome_html"
compress="gzip" />
<if expr="not is_android and not is_ios">
<structure name="IDR_WEBUI_HTML_SEARCH_HIGHLIGHT_UTILS"
file="html/search_highlight_utils.html" type="chrome_html"
compress="gzip" />
</if>
<structure name="IDR_WEBUI_JS_ACTION_LINK"
file="js/action_link.js" type="chrome_html" compress="gzip" />
......@@ -531,6 +536,12 @@ without changes to the corresponding grd file. -->
file="js/webui_resource_test.js" type="chrome_html"
compress="gzip" />
<if expr="not is_android and not is_ios">
<structure name="IDR_WEBUI_JS_SEARCH_HIGHLIGHT_UTILS"
file="js/search_highlight_utils.js" type="chrome_html"
compress="gzip" />
</if>
<if expr="not is_android">
<part file="cr_components/cr_components_resources.grdp" />
<part file="cr_elements_resources.grdp" />
<part file="cr_polymer_resources.grdp" />
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment