diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css
index 4338cff6d207..e9ff2991adb5 100644
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -849,11 +849,6 @@ toolbarspring {
}
}
-#editBMPanel_tagsSelector {
- /* override default listbox width from xul.css */
- width: auto;
-}
-
menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
display: none;
}
diff --git a/browser/base/content/webext-panels.xul b/browser/base/content/webext-panels.xul
index 97bcd7c5d914..b821c6ce6510 100644
--- a/browser/base/content/webext-panels.xul
+++ b/browser/base/content/webext-panels.xul
@@ -5,6 +5,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
diff --git a/browser/components/places/content/editBookmark.js b/browser/components/places/content/editBookmark.js
index d21c90afe7c2..a72b733c9593 100644
--- a/browser/components/places/content/editBookmark.js
+++ b/browser/components/places/content/editBookmark.js
@@ -747,8 +747,6 @@ var gEditItemOverlay = {
if (tagsSelectorRow.collapsed)
return;
- // Save the current scroll position and restore it after the rebuild.
- let firstIndex = tagsSelector.getIndexOfFirstVisibleRow();
let selectedIndex = tagsSelector.selectedIndex;
let selectedTag = selectedIndex >= 0 ? tagsSelector.selectedItem.label
: null;
@@ -759,24 +757,22 @@ var gEditItemOverlay = {
let tagsInField = this._getTagsArrayFromTagsInputField();
let allTags = PlacesUtils.tagging.allTags;
- for (let tag of allTags) {
- let elt = document.createElement("listitem");
- elt.setAttribute("type", "checkbox");
- elt.setAttribute("label", tag);
+ let fragment = document.createDocumentFragment();
+ for (var i = 0; i < allTags.length; i++) {
+ let tag = allTags[i];
+ let elt = document.createElement("richlistitem");
+ elt.appendChild(document.createElement("image"));
+ let label = document.createElement("label");
+ label.setAttribute("value", tag);
+ elt.appendChild(label);
if (tagsInField.includes(tag))
elt.setAttribute("checked", "true");
- tagsSelector.appendChild(elt);
+ fragment.appendChild(elt);
if (selectedTag === tag)
- selectedIndex = tagsSelector.getIndexOfItem(elt);
+ selectedIndex = i;
}
+ tagsSelector.appendChild(fragment);
- // Restore position.
- // The listbox allows to scroll only if the required offset doesn't
- // overflow its capacity, thus need to adjust the index for removals.
- firstIndex =
- Math.min(firstIndex,
- tagsSelector.itemCount - tagsSelector.getNumberOfVisibleRows());
- tagsSelector.scrollToIndex(firstIndex);
if (selectedIndex >= 0 && tagsSelector.itemCount > 0) {
selectedIndex = Math.min(selectedIndex, tagsSelector.itemCount - 1);
tagsSelector.selectedIndex = selectedIndex;
@@ -796,12 +792,17 @@ var gEditItemOverlay = {
this._rebuildTagsSelectorList();
// This is a no-op if we've added the listener.
- tagsSelector.addEventListener("CheckboxStateChange", this);
+ tagsSelector.addEventListener("mousedown", this);
+ tagsSelector.addEventListener("keypress", this);
} else {
expander.className = "expander-down";
expander.setAttribute("tooltiptext",
expander.getAttribute("tooltiptextdown"));
tagsSelectorRow.collapsed = true;
+
+ // This is a no-op if we've removed the listener.
+ tagsSelector.removeEventListener("mousedown", this);
+ tagsSelector.removeEventListener("keypress", this);
}
},
@@ -845,25 +846,24 @@ var gEditItemOverlay = {
},
// EventListener
- handleEvent(aEvent) {
- switch (aEvent.type) {
- case "CheckboxStateChange":
- // Update the tags field when items are checked/unchecked in the listbox
- let tags = this._getTagsArrayFromTagsInputField();
- let tagCheckbox = aEvent.target;
-
- let curTagIndex = tags.indexOf(tagCheckbox.label);
- let tagsSelector = this._element("tagsSelector");
- tagsSelector.selectedItem = tagCheckbox;
-
- if (tagCheckbox.checked) {
- if (curTagIndex == -1)
- tags.push(tagCheckbox.label);
- } else if (curTagIndex != -1) {
- tags.splice(curTagIndex, 1);
+ handleEvent(event) {
+ switch (event.type) {
+ case "mousedown":
+ if (event.button == 0) {
+ // Make sure the event is triggered on an item and not the empty space.
+ let item = event.target.closest("richlistbox,richlistitem");
+ if (item.localName == "richlistitem") {
+ this.toggleItemCheckbox(item);
+ }
+ }
+ break;
+ case "keypress":
+ if (event.key == " ") {
+ let item = event.target.currentItem;
+ if (item) {
+ this.toggleItemCheckbox(item);
+ }
}
- this._element("tagsField").value = tags.join(", ");
- this._updateTags();
break;
case "unload":
this.uninitPanel(false);
@@ -871,6 +871,27 @@ var gEditItemOverlay = {
}
},
+ toggleItemCheckbox(item) {
+ // Update the tags field when items are checked/unchecked in the listbox
+ let tags = this._getTagsArrayFromTagsInputField();
+
+ let curTagIndex = tags.indexOf(item.label);
+ let tagsSelector = this._element("tagsSelector");
+ tagsSelector.selectedItem = item;
+
+ if (!item.hasAttribute("checked")) {
+ item.setAttribute("checked", "true");
+ if (curTagIndex == -1)
+ tags.push(item.label);
+ } else {
+ item.removeAttribute("checked");
+ if (curTagIndex != -1)
+ tags.splice(curTagIndex, 1);
+ }
+ this._element("tagsField").value = tags.join(", ");
+ this._updateTags();
+ },
+
_initTagsField() {
let tags;
if (this._paneInfo.isURI)
diff --git a/browser/components/places/content/editBookmarkPanel.inc.xul b/browser/components/places/content/editBookmarkPanel.inc.xul
index 9de94dd896e3..5ae7df24e1ef 100644
--- a/browser/components/places/content/editBookmarkPanel.inc.xul
+++ b/browser/components/places/content/editBookmarkPanel.inc.xul
@@ -107,9 +107,10 @@
-
+ collapsed="true">
+
parentItemRect.top < y && y < parentItemRect.bottom;
+
+ // Partially visible items are also considered visible.
+ return pointInView(itemRect.top) || pointInView(itemRect.bottom);
+}
+
add_task(async function() {
await PlacesUtils.bookmarks.eraseEverything();
let tags = ["a", "b", "c", "d", "e", "f", "g",
@@ -63,32 +72,30 @@ add_task(async function() {
isnot(listItem, null, "Valid listItem found");
tagsSelector.ensureElementIsVisible(listItem);
- let visibleIndex = tagsSelector.getIndexOfFirstVisibleRow();
+ let scrollTop = tagsSelector.scrollTop;
- ok(listItem.checked, "Item is checked " + i);
+ ok(listItem.hasAttribute("checked"), "Item is checked " + i);
let selectedTag = listItem.label;
// Uncheck the tag.
let promiseNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (id, property) => property == "tags");
- listItem.checked = false;
+ EventUtils.synthesizeMouseAtCenter(listItem.firstChild, {});
await promiseNotification;
- is(visibleIndex, tagsSelector.getIndexOfFirstVisibleRow(),
- "Scroll position did not change");
+ is(scrollTop, tagsSelector.scrollTop, "Scroll position did not change");
// The listbox is rebuilt, so we have to get the new element.
let newItem = tagsSelector.selectedItem;
isnot(newItem, null, "Valid new listItem found");
- ok(!newItem.checked, "New listItem is unchecked " + i);
+ ok(!newItem.hasAttribute("checked"), "New listItem is unchecked " + i);
is(newItem.label, selectedTag, "Correct tag is still selected");
// Check the tag.
promiseNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (id, property) => property == "tags");
- newItem.checked = true;
+ EventUtils.synthesizeMouseAtCenter(newItem.firstChild, {});
await promiseNotification;
- is(visibleIndex, tagsSelector.getIndexOfFirstVisibleRow(),
- "Scroll position did not change");
+ is(scrollTop, tagsSelector.scrollTop, "Scroll position did not change");
}
// Remove the second bookmark, then nuke some of the tags.
@@ -101,28 +108,24 @@ add_task(async function() {
isnot(listItem, null, "Valid listItem found");
tagsSelector.ensureElementIsVisible(listItem);
- let firstVisibleTag = tags[tagsSelector.getIndexOfFirstVisibleRow()];
+ let items = [...tagsSelector.children];
+ let topTag = items.find(e => scrolledIntoView(e, tagsSelector)).label;
- ok(listItem.checked, "Item is checked " + i);
+ ok(listItem.hasAttribute("checked"), "Item is checked " + i);
// Uncheck the tag.
let promiseNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (id, property) => property == "tags");
- listItem.checked = false;
+ EventUtils.synthesizeMouseAtCenter(listItem.firstChild, {});
await promiseNotification;
- // Ensure the first visible tag is still visible in the list.
- let firstVisibleIndex = tagsSelector.getIndexOfFirstVisibleRow();
- let lastVisibleIndex = firstVisibleIndex + tagsSelector.getNumberOfVisibleRows() - 1;
- let expectedTagIndex = tags.indexOf(firstVisibleTag);
- ok(expectedTagIndex >= firstVisibleIndex &&
- expectedTagIndex <= lastVisibleIndex,
- "Scroll position is correct");
-
// The listbox is rebuilt, so we have to get the new element.
+ let topItem = [...tagsSelector.children].find(e => e.label == topTag);
+ ok(scrolledIntoView(topItem, tagsSelector), "Scroll position is correct");
+
let newItem = tagsSelector.selectedItem;
isnot(newItem, null, "Valid new listItem found");
- ok(newItem.checked, "New listItem is checked " + i);
+ ok(newItem.hasAttribute("checked"), "New listItem is checked " + i);
is(tagsSelector.selectedItem.label,
tags[Math.min(i + 1, tags.length - 2)],
"The next tag is now selected");
diff --git a/browser/components/places/tests/browser/browser_editBookmark_tags_liveUpdate.js b/browser/components/places/tests/browser/browser_editBookmark_tags_liveUpdate.js
index a29428cd3891..8c7a5f86ae5a 100644
--- a/browser/components/places/tests/browser/browser_editBookmark_tags_liveUpdate.js
+++ b/browser/components/places/tests/browser/browser_editBookmark_tags_liveUpdate.js
@@ -9,7 +9,7 @@ function checkTagsSelector(aAvailableTags, aCheckedTags) {
"Found expected number of tags in the tags selector");
Array.prototype.forEach.call(children, function(aChild) {
- let tag = aChild.getAttribute("label");
+ let tag = aChild.querySelector("label").getAttribute("value");
ok(true, "Found tag '" + tag + "' in the selector");
ok(aAvailableTags.includes(tag), "Found expected tag");
let checked = aChild.getAttribute("checked") == "true";
diff --git a/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.js b/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.js
index e9b043e53964..b3d8e6aef00d 100644
--- a/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.js
+++ b/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.js
@@ -23,14 +23,6 @@ add_task(async function() {
ok(checkbox.checked, "Checkbox is checked");
await checkPageScrolling(container, "checkbox");
- // Test listbox
- let listbox = doc.getElementById("listbox");
- let listitem = doc.getElementById("listitem");
- listbox.focus();
- EventUtils.sendString(" ");
- ok(listitem.selected, "Listitem is selected");
- await checkPageScrolling(container, "listbox");
-
// Test radio
let radiogroup = doc.getElementById("radiogroup");
radiogroup.focus();
diff --git a/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul b/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul
index 59b644c8fa24..4945102f598f 100644
--- a/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul
+++ b/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul
@@ -15,13 +15,6 @@
-
-
-
-
-
-
-
diff --git a/browser/components/preferences/languages.js b/browser/components/preferences/languages.js
index c8f417212627..2062630d3e57 100644
--- a/browser/components/preferences/languages.js
+++ b/browser/components/preferences/languages.js
@@ -22,18 +22,13 @@ var gLanguagesDialog = {
_selectedItemID: null,
- init() {
- if (!this._availableLanguagesList.length)
- this._loadAvailableLanguages();
- },
+ onLoad() {
+ Preferences.get("intl.accept_languages").on("change",
+ () => this._readAcceptLanguages().catch(Cu.reportError));
- // Ugly hack used to trigger extra reflow in order to work around XUL bug 1194844;
- // see bug 1194346.
- forceReflow() {
- this._activeLanguages.style.fontKerning = "none";
- setTimeout(() => {
- this._activeLanguages.style.removeProperty("font-kerning");
- }, 0);
+ if (!this._availableLanguagesList.length) {
+ document.mozSubdialogReady = this._loadAvailableLanguages();
+ }
},
get _activeLanguages() {
@@ -44,7 +39,7 @@ var gLanguagesDialog = {
return document.getElementById("availableLanguages");
},
- _loadAvailableLanguages() {
+ async _loadAvailableLanguages() {
// This is a parser for: resource://gre/res/language.properties
// The file is formatted like so:
// ab[-cd].accept=true|false
@@ -85,7 +80,8 @@ var gLanguagesDialog = {
this._availableLanguagesList.push(li);
}
- this._buildAvailableLanguageList();
+ await this._buildAvailableLanguageList();
+ await this._readAcceptLanguages();
},
async _buildAvailableLanguageList() {
@@ -132,23 +128,25 @@ var gLanguagesDialog = {
this._availableLanguages.setAttribute("label", this._availableLanguages.getAttribute("placeholder"));
},
- async readAcceptLanguages() {
+ async _readAcceptLanguages() {
while (this._activeLanguages.hasChildNodes())
this._activeLanguages.firstChild.remove();
var selectedIndex = 0;
var preference = Preferences.get("intl.accept_languages");
if (preference.value == "")
- return undefined;
+ return;
var languages = preference.value.toLowerCase().split(/\s*,\s*/);
for (var i = 0; i < languages.length; ++i) {
- var listitem = document.createElement("listitem");
+ var listitem = document.createElement("richlistitem");
+ var label = document.createElement("label");
+ listitem.appendChild(label);
listitem.id = languages[i];
if (languages[i] == this._selectedItemID)
selectedIndex = i;
this._activeLanguages.appendChild(listitem);
var localeName = this._getLocaleName(languages[i]);
- document.l10n.setAttributes(listitem, "languages-code-format", {
+ document.l10n.setAttributes(label, "languages-active-code-format", {
locale: localeName,
code: languages[i],
});
@@ -171,12 +169,6 @@ var gLanguagesDialog = {
// Update states of accept-language list and buttons according to
// privacy.resistFingerprinting and privacy.spoof_english.
this.readSpoofEnglish();
-
- return undefined;
- },
-
- writeAcceptLanguages() {
- return undefined;
},
onAvailableLanguageSelect() {
@@ -210,7 +202,7 @@ var gLanguagesDialog = {
this._availableLanguages.selectedItem = null;
// Rebuild the available list with the added item removed...
- this._buildAvailableLanguageList();
+ this._buildAvailableLanguageList().catch(Cu.reportError);
},
removeLanguage() {
@@ -237,7 +229,7 @@ var gLanguagesDialog = {
var preference = Preferences.get("intl.accept_languages");
preference.value = string;
- this._buildAvailableLanguageList();
+ this._buildAvailableLanguageList().catch(Cu.reportError);
},
_getLocaleName(localeCode) {
@@ -350,8 +342,3 @@ var gLanguagesDialog = {
return document.getElementById("spoofEnglish").checked ? 2 : 1;
}
};
-
-// These focus and resize handlers hack around XUL bug 1194844
-// by triggering extra reflow (see bug 1194346).
-window.addEventListener("focus", () => gLanguagesDialog.forceReflow());
-window.addEventListener("resize", () => gLanguagesDialog.forceReflow());
diff --git a/browser/components/preferences/languages.xul b/browser/components/preferences/languages.xul
index d33143ab2387..d3f98234f269 100644
--- a/browser/components/preferences/languages.xul
+++ b/browser/components/preferences/languages.xul
@@ -15,7 +15,7 @@
buttons="accept,cancel,help"
persist="lastSelected screenX screenY"
role="dialog"
- onload="gLanguagesDialog.init();"
+ onload="gLanguagesDialog.onLoad();"
helpTopic="prefs-languages"
ondialoghelp="openPrefsHelp()">
@@ -49,11 +49,9 @@
-
+