Bug 1420761 - Handle new localization API in Preferences Search. r=jaws

MozReview-Commit-ID: 8J1siQtFn1t

--HG--
extra : rebase_source : 98f647b6e677d8e0d83b23ad7354599f62fc745f
This commit is contained in:
Zibi Braniecki 2018-03-19 21:17:45 -07:00
Родитель 94e261de76
Коммит e470fe67ab
3 изменённых файлов: 137 добавлений и 8 удалений

Просмотреть файл

@ -9,6 +9,9 @@ var gSearchResultsPane = {
listSearchTooltips: new Set(),
listSearchMenuitemIndicators: new Set(),
searchInput: null,
// A map of DOM Elements to a string of keywords used in search
// XXX: We should invalidate this cache on `intl:app-locales-changed`
searchKeywords: new WeakMap(),
inited: false,
init() {
@ -264,7 +267,7 @@ var gSearchResultsPane = {
if (!child.classList.contains("header") &&
!child.classList.contains("subcategory") &&
this.searchWithinNode(child, this.query)) {
await this.searchWithinNode(child, this.query)) {
child.hidden = false;
child.classList.remove("visually-hidden");
@ -327,7 +330,7 @@ var gSearchResultsPane = {
* @returns boolean
* Returns true when found in at least one childNode, false otherwise
*/
searchWithinNode(nodeObject, searchPhrase) {
async searchWithinNode(nodeObject, searchPhrase) {
let matchesFound = false;
if (nodeObject.childElementCount == 0 ||
nodeObject.tagName == "label" ||
@ -366,8 +369,20 @@ var gSearchResultsPane = {
let valueResult = nodeObject.tagName !== "menuitem" ?
this.queryMatchesContent(nodeObject.getAttribute("value"), searchPhrase) : false;
// Searching some elements, such as xul:button, buttons to open subdialogs.
let keywordsResult = this.queryMatchesContent(nodeObject.getAttribute("searchkeywords"), searchPhrase);
// Searching some elements, such as xul:button, buttons to open subdialogs
// using l10n ids.
let keywordsResult =
nodeObject.hasAttribute("search-l10n-ids") &&
await this.matchesSearchL10nIDs(nodeObject, searchPhrase);
if (!keywordsResult) {
// Searching some elements, such as xul:button, buttons to open subdialogs
// using searchkeywords attribute.
keywordsResult =
!keywordsResult &&
nodeObject.hasAttribute("searchkeywords") &&
this.queryMatchesContent(nodeObject.getAttribute("searchkeywords"), searchPhrase);
}
// Creating tooltips for buttons
if (keywordsResult && (nodeObject.tagName === "button" || nodeObject.tagName == "menulist")) {
@ -398,12 +413,12 @@ var gSearchResultsPane = {
if (nodeObject.tagName == "deck" && nodeObject.id != "historyPane") {
let index = nodeObject.selectedIndex;
if (index != -1) {
let result = this.searchChildNodeIfVisible(nodeObject, index, searchPhrase);
let result = await this.searchChildNodeIfVisible(nodeObject, index, searchPhrase);
matchesFound = matchesFound || result;
}
} else {
for (let i = 0; i < nodeObject.childNodes.length; i++) {
let result = this.searchChildNodeIfVisible(nodeObject, i, searchPhrase);
let result = await this.searchChildNodeIfVisible(nodeObject, i, searchPhrase);
matchesFound = matchesFound || result;
}
}
@ -421,10 +436,10 @@ var gSearchResultsPane = {
* @returns boolean
* Returns true when found the specific childNode, false otherwise
*/
searchChildNodeIfVisible(nodeObject, index, searchPhrase) {
async searchChildNodeIfVisible(nodeObject, index, searchPhrase) {
let result = false;
if (!nodeObject.childNodes[index].hidden && nodeObject.getAttribute("data-hidden-from-search") !== "true") {
result = this.searchWithinNode(nodeObject.childNodes[index], searchPhrase);
result = await this.searchWithinNode(nodeObject.childNodes[index], searchPhrase);
// Creating tooltips for menulist element
if (result && nodeObject.tagName === "menulist") {
this.listSearchTooltips.add(nodeObject);
@ -433,6 +448,56 @@ var gSearchResultsPane = {
return result;
},
/**
* Search for a phrase in l10n messages associated with the element.
*
* @param Node nodeObject
* The parent DOM Element
* @param String searchPhrase
* @returns boolean
* true when the text content contains the query string else false
*/
async matchesSearchL10nIDs(nodeObject, searchPhrase) {
if (!this.searchKeywords.has(nodeObject)) {
// The `search-l10n-ids` attribute is a comma-separated list of
// l10n ids. It may also uses a dot notation to specify an attribute
// of the message to be used.
//
// Example: "containers-add-button.label, user-context-personal"
//
// The result is an array of arrays of l10n ids and optionally attribute names.
//
// Example: [["containers-add-button", "label"], ["user-context-personal"]]
const refs = nodeObject.getAttribute("search-l10n-ids")
.split(",")
.map(s => s.trim().split(".")).filter(s => s[0].length > 0);
const messages = await document.l10n.formatMessages(refs.map(ref => [ref[0]]));
// Map the localized messages taking value or a selected attribute and
// building a string of concatenated translated strings out of it.
let keywords = messages.map((msg, i) => {
if (msg === null) {
console.warn(`Missing search l10n id "${refs[i][0]}"`);
return null;
}
if (refs[i][1]) {
let attr = msg.attrs.find(a => a.name === refs[i][1]);
if (attr) {
return attr.value;
}
return null;
}
return msg.value;
}).filter(keyword => keyword !== null).join(" ");
this.searchKeywords.set(nodeObject, keywords);
return this.queryMatchesContent(keywords, searchPhrase);
}
return this.queryMatchesContent(this.searchKeywords.get(nodeObject), searchPhrase);
},
/**
* Inserting a div structure infront of the DOM element matched textContent.
* Then calculation the offsets to position the tooltip in the correct place.

Просмотреть файл

@ -142,6 +142,7 @@ add_task(async function search_with_nothing_found() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", { leaveOpen: true });
let noResultsEl = gBrowser.contentDocument.querySelector("#no-results-message");
let sorryMsgQueryEl = gBrowser.contentDocument.getElementById("sorry-message-query");
is_element_hidden(noResultsEl, "Should not be in search results yet");
@ -158,6 +159,7 @@ add_task(async function search_with_nothing_found() {
await searchCompletedPromise;
is_element_visible(noResultsEl, "Should be in search results");
is(sorryMsgQueryEl.textContent, query, "sorry-message-query should contain the query");
// Takes search off
searchCompletedPromise = BrowserTestUtils.waitForEvent(
@ -169,6 +171,7 @@ add_task(async function search_with_nothing_found() {
await searchCompletedPromise;
is_element_hidden(noResultsEl, "Should not be in search results");
is(sorryMsgQueryEl.textContent.length, 0, "sorry-message-query should be empty");
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

Просмотреть файл

@ -65,3 +65,64 @@ add_task(async function() {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test that we search using `search-l10n-ids`.
*
* The test uses element `browserContainersSettings` and
* l10n id `language-and-appearance-header` and expects the element
* to be matched on the first word from the l10n id value ("Language" in en-US).
*/
add_task(async function() {
let l10nId = "language-and-appearance-header";
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
// First, lets make sure that the element is not matched without
// `search-l10n-ids`.
{
let searchInput = gBrowser.contentDocument.getElementById("searchInput");
let bcsElem = gBrowser.contentDocument.getElementById("browserContainersSettings");
is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
"Search input should be focused when visiting preferences");
ok(!bcsElem.getAttribute("search-l10n-ids").includes(l10nId),
"browserContainersSettings element should not contain the l10n id here.");
let query = "Language";
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
is_element_hidden(bcsElem, "browserContainersSettings should not be in search results");
}
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
// Now, let's add the l10n id to the element and perform the same search again.
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
{
let searchInput = gBrowser.contentDocument.getElementById("searchInput");
is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
"Search input should be focused when visiting preferences");
let bcsElem = gBrowser.contentDocument.getElementById("browserContainersSettings");
bcsElem.setAttribute("search-l10n-ids", l10nId);
let query = "Language";
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
is_element_visible(bcsElem, "browserContainersSettings should be in search results");
}
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});