Bug 1933104 - Update tree-view rows at most once per frame. r=aleca,mkmelin

Differential Revision: https://phabricator.services.mozilla.com/D230066

--HG--
extra : amend_source : b0c8c2ea4312a7ff9c220c97127a9d8ba5c2b205
This commit is contained in:
Geoff Lankow 2024-11-26 12:22:53 +02:00
Родитель 28d8dc4a61
Коммит f1f5234eb1
27 изменённых файлов: 279 добавлений и 269 удалений

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

@ -4398,8 +4398,10 @@ var threadPane = {
threadTree.invalidate();
break;
case "custom-column-refreshed":
// Invalidate only the column specified in data.
threadTree.invalidate(data);
// Invalidate the whole thing. This used to refresh just the column,
// but now that filling the cells happens asynchronously, that's too
// complicated. Kept for add-on compatibility.
threadTree.invalidate();
break;
case "custom-column-added":
this.addCustomColumn(data);

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

@ -417,14 +417,10 @@ class AutoTreeViewTableRow extends TreeViewTableRow {
}
}
get index() {
return super.index;
}
_fillRow() {
super._fillRow();
set index(index) {
super.index = index;
this.dataset.properties = this.view.getRowProperties(index);
this.dataset.properties = this.view.getRowProperties(this._index);
for (const column of this.list.table.columns) {
const cell = this.querySelector(`.${column.id.toLowerCase()}-column`);
@ -433,7 +429,7 @@ class AutoTreeViewTableRow extends TreeViewTableRow {
continue;
}
const text = this.view.getCellText(index, column.id);
const text = this.view.getCellText(this._index, column.id);
cell.textContent = text;
if (column.l10n.cell) {
document.l10n.setAttributes(cell, column.l10n.cell, { title: text });

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

@ -740,16 +740,9 @@ export class TreeView extends HTMLElement {
* Updates all existing rows in place, without removing all the rows and
* starting again. This can be used if the row element class hasn't changed
* and its `index` setter is capable of handling any modifications required.
*
* @param {string} [columnId] - Optional column id, to limit invalidation to a
* single column.
*/
invalidate(columnId) {
this.invalidateRange(
this.#firstBufferRowIndex,
this.#lastBufferRowIndex,
columnId
);
invalidate() {
this.invalidateRange(this.#firstBufferRowIndex, this.#lastBufferRowIndex);
}
/**
@ -758,20 +751,16 @@ export class TreeView extends HTMLElement {
* on its own.
*
* @param {integer} index
* @param {string} [columnId] - Optional column id, to limit invalidation to a
* single column.
*/
#doInvalidateRow(index, columnId) {
#doInvalidateRow(index) {
const rowCount = this._view?.rowCount ?? 0;
const row = this.getRowAtIndex(index);
if (row) {
if (index >= rowCount) {
this._removeRowAtIndex(index);
} else {
row.invalidateSingleColumn = columnId;
row.index = index;
row.selected = this._selection.isSelected(index);
row.invalidateSingleColumn = null;
}
} else if (
index >= this.#firstBufferRowIndex &&
@ -786,17 +775,15 @@ export class TreeView extends HTMLElement {
*
* @param {integer} startIndex
* @param {integer} endIndex
* @param {string} [columnId] - Optional column id, to limit invalidation to a
* single column.
*/
invalidateRange(startIndex, endIndex, columnId) {
invalidateRange(startIndex, endIndex) {
for (
let index = Math.max(startIndex, this.#firstBufferRowIndex),
last = Math.min(endIndex, this.#lastBufferRowIndex);
index <= last;
index++
) {
this.#doInvalidateRow(index, columnId);
this.#doInvalidateRow(index);
}
this._ensureVisibleRowsAreDisplayed();
}
@ -2724,19 +2711,16 @@ export class TreeViewTableRow extends HTMLTableRowElement {
this.setAttribute("aria-selected", !!this.selected);
}
/**
* Id of a column. If set, the index setter will only update the specified
* column.
*
* @type {?string}
*/
invalidateSingleColumn = null;
/**
* The 0-based position of this row in the list. Override this setter to
* fill layout based on values from the list's view. Always call back to
* this class's getter/setter when inheriting.
*
* Setting the index doesn't instantly fill the row. That happens at the
* next animation frame using the most recently set index. Tests that set
* the index will also need to wait for an animation frame before checking
* the row's content.
*
* @note Don't short-circuit the setter if the given index is equal to the
* existing index. Rows can be reused to display new data at the same index.
*
@ -2746,27 +2730,50 @@ export class TreeViewTableRow extends HTMLTableRowElement {
return this._index;
}
#animationFrame = null;
set index(index) {
this._index = index;
// Wait before filling the row. This setter could be called many times
// before it even appears on the screen, and calling (potentially very
// expensive) code each time would be a waste.
if (!this.#animationFrame) {
this.#animationFrame = requestAnimationFrame(() => {
this.#animationFrame = null;
// The row may no longer be attached to the tree. Don't waste time
// filling it in that case.
if (this.parentNode) {
this._fillRow();
}
});
}
}
/**
* Fill out the row with content based on the current value of `this._index`.
* Subclasses should override this setter and call back to it.
*/
_fillRow() {
this.setAttribute(
"role",
this.list.table.body.getAttribute("role") === "treegrid"
? "row"
: "option"
);
this.setAttribute("aria-posinset", index + 1);
this.id = `${this.list.id}-row${index}`;
this.setAttribute("aria-posinset", this._index + 1);
this.id = `${this.list.id}-row${this._index}`;
const isGroup = this.view.isContainer(index);
const isGroup = this.view.isContainer(this._index);
this.classList.toggle("children", isGroup);
const isGroupOpen = this.view.isContainerOpen(index);
const isGroupOpen = this.view.isContainerOpen(this._index);
if (isGroup) {
this.setAttribute("aria-expanded", isGroupOpen);
} else {
this.removeAttribute("aria-expanded");
}
this.classList.toggle("collapsed", !isGroupOpen);
this._index = index;
const table = this.closest("table");
for (const column of table.columns) {
@ -2803,7 +2810,7 @@ export class TreeViewTableRow extends HTMLTableRowElement {
}
document.l10n.setAttributes(
img,
this.list._selection.isSelected(index)
this.list._selection.isSelected(this._index)
? "tree-list-view-row-deselect"
: "tree-list-view-row-select"
);

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

@ -49,12 +49,8 @@ class ThreadCard extends TreeViewTableRow {
this.sortHeaderDetails = this.querySelector(".sort-header-details");
}
get index() {
return super.index;
}
set index(index) {
super.index = index;
_fillRow() {
super._fillRow();
// XPCOM calls here must be keep to a minimum. Collect all of the
// required data in one go.
@ -62,7 +58,7 @@ class ThreadCard extends TreeViewTableRow {
const threadLevel = {};
const cellTexts = this.view.cellDataForColumns(
index,
this._index,
window.threadPane.cardColumns,
properties,
threadLevel

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

@ -31,22 +31,11 @@ class ThreadRow extends TreeViewTableRow {
);
}
get index() {
return super.index;
}
set index(index) {
super.index = index;
// Check if a only a single column should be updated.
const columns = this.invalidateSingleColumn
? window.threadPane.columns.filter(
column => column.id == this.invalidateSingleColumn
)
: window.threadPane.columns;
_fillRow() {
super._fillRow();
const textColumns = [];
for (const column of columns) {
for (const column of window.threadPane.columns) {
// No need to update the text of this cell if it's hidden, the selection
// column, or a non-custom icon column that doesn't match a specific flag.
if (column.hidden || (!column.custom && column.icon) || column.select) {
@ -60,7 +49,7 @@ class ThreadRow extends TreeViewTableRow {
const properties = {};
const threadLevel = {};
const cellTexts = this.view.cellDataForColumns(
index,
this._index,
textColumns,
properties,
threadLevel
@ -75,7 +64,7 @@ class ThreadRow extends TreeViewTableRow {
this.dataset.properties = properties.value.trim();
for (const column of columns) {
for (const column of window.threadPane.columns) {
// Skip this column if it's hidden.
if (column.hidden) {
continue;

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

@ -138,6 +138,7 @@ add_task(async function testSwitchToCardsView() {
);
EventUtils.synthesizeKey("KEY_Escape", {});
await hiddenPromise;
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
Assert.equal(
threadTree.getAttribute("rows"),

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

@ -70,8 +70,9 @@ add_task(async function test_dummy_row_in_table_view() {
() => threadTree.getAttribute("rows") == "thread-row",
"The tree view switched to a table layout"
);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
Assert.deepEqual(
Assert.equal(
threadTree.getRowAtIndex(0).querySelector(".subject-line span").textContent,
`Older (${folderAMessages.length}/${folderAMessages.length})`,
"The subject text with the unread and total counter should match"
@ -84,6 +85,7 @@ add_task(async function test_dummy_row_in_table_view() {
// from this loop.
for (let i = 1; i < folderAMessages.length; i++) {
folderAMessages[i].markRead(true);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
Assert.equal(
threadTree.getRowAtIndex(0).querySelector(".subject-line span")
.textContent,
@ -92,6 +94,7 @@ add_task(async function test_dummy_row_in_table_view() {
}
folderAMessages[0].markRead(true);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
Assert.equal(
threadTree.getRowAtIndex(0).querySelector(".subject-line span").textContent,
`Older (${folderAMessages.length})`

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

@ -675,6 +675,7 @@ add_task(async function testMultipleMessages() {
!threadTree.getRowAtIndex(threadTree.currentIndex),
"Current row is scrolled out of view"
);
await new Promise(resolve => window.requestAnimationFrame(resolve));
EventUtils.synthesizeMouseAtCenter(
threadTree,

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

@ -142,6 +142,7 @@ add_task(async function () {
emily.displayName = "I'm Emily!";
book.modifyCard(emily);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
row = about3Pane.threadTree.getRowAtIndex(2);
Assert.equal(
@ -179,6 +180,7 @@ add_task(async function () {
emily.displayName = "";
book.modifyCard(emily);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
row = about3Pane.threadTree.getRowAtIndex(2);
Assert.equal(
@ -216,6 +218,7 @@ add_task(async function () {
emily.displayName = "I'm Emily!";
book.modifyCard(emily);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
row = about3Pane.threadTree.getRowAtIndex(2);
Assert.equal(
@ -253,6 +256,7 @@ add_task(async function () {
felix.displayName = "Felix's Flower Co.";
book.modifyCard(felix);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
row = about3Pane.threadTree.getRowAtIndex(2);
Assert.equal(
@ -275,6 +279,7 @@ add_task(async function () {
felix.displayName = "";
book.modifyCard(felix);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
row = about3Pane.threadTree.getRowAtIndex(2);
Assert.equal(
@ -456,6 +461,7 @@ add_task(async function () {
// Prefer email only. Changing the preference causes the message to reload.
Services.prefs.setIntPref("mail.addressDisplayFormat", 1);
await BrowserTestUtils.browserLoaded(messagePaneBrowser);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
row = about3Pane.threadTree.getRowAtIndex(3);
Assert.equal(
@ -472,6 +478,7 @@ add_task(async function () {
// Prefer name only. Changing the preference causes the message to reload.
Services.prefs.setIntPref("mail.addressDisplayFormat", 2);
await BrowserTestUtils.browserLoaded(messagePaneBrowser);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
row = about3Pane.threadTree.getRowAtIndex(3);
Assert.equal(

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

@ -384,6 +384,7 @@ async function subtest() {
() => threadTree.table.body.rows.length == 15,
"waiting for all of the table rows"
);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
const dbView = about3Pane.gDBView;
const subjects = [];
@ -489,6 +490,7 @@ async function _doDelete(callback, index, expectedLoad) {
}
threadTree.removeEventListener("select", onSelect);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
}
async function doDeleteCommand(expectedLoad) {

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

@ -581,6 +581,7 @@ add_task(async function testThreadTreeA11yRoles() {
() => threadTree.getRowAtIndex(0),
"row0 should become available"
);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
Assert.equal(
threadTree.getRowAtIndex(0).getAttribute("role"),
"row",
@ -602,6 +603,7 @@ add_task(async function testThreadTreeA11yRoles() {
() => threadTree.getRowAtIndex(0),
"row0 should become available"
);
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
Assert.equal(
threadTree.getRowAtIndex(0).getAttribute("role"),
"row",
@ -623,6 +625,7 @@ add_task(async function test_read_new_properties() {
// Clicking the twisty to collapse a row should update the message display.
goDoCommand("cmd_expandAllThreads");
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
threadTree.selectedIndex = 5;
await messageLoaded(10);

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

@ -35,7 +35,7 @@ add_setup(async function () {
messagePaneVisible: false,
folderURI: testFolder.URI,
});
await new Promise(resolve => requestAnimationFrame(resolve));
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
document.getElementById("toolbar-menubar").removeAttribute("autohide");
@ -132,7 +132,10 @@ add_task(async function testColumnHeaderClick() {
const targetMessage = messagesByDate[49];
info(`selecting message "${targetMessage.subject}"`);
threadTree.scrollToIndex(49, true);
await new Promise(resolve => requestAnimationFrame(resolve));
// Wait once for the scroll...
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
// ... and once for the row to be filled.
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
threadTree.selectedIndex = 49;
verifySelection([49], [targetMessage.subject]);
@ -174,7 +177,10 @@ add_task(async function testColumnHeaderClick() {
`selecting messages "${targetMessages.map(m => m.subject).join('", "')}"`
);
threadTree.scrollToIndex(83, true);
await new Promise(resolve => requestAnimationFrame(resolve));
// Wait once for the scroll...
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
// ... and once for the rows to be filled.
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
threadTree.selectedIndices = [80, 81, 83];
verifySelection(
[80, 81, 83],

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

@ -300,6 +300,7 @@ add_task(async function testIconsUnThreaded() {
);
goDoCommand("cmd_sort", { target: { value: "unthreaded" } });
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
// Switched to unthreaded and test again.
threadTree.selectedIndex = 0;
@ -349,7 +350,7 @@ async function checkContextMenu(index, expectedStates, itemToActivate) {
contextMenu.hidePopup();
}
await BrowserTestUtils.waitForPopupEvent(contextMenu, "hidden");
await new Promise(resolve => window.requestAnimationFrame(resolve));
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
}
async function checkMessageMenu(expectedStates) {

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

@ -205,6 +205,7 @@ add_task(async function () {
checkPersistedValue("sortDirection", "");
EventUtils.synthesizeMouseAtCenter(headerButtons[1], {}, win);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["violet", "africa", "", "", ""],
["yellow", "antarctica", "", "", ""],
@ -219,6 +220,7 @@ add_task(async function () {
checkPersistedValue("sortDirection", "ascending");
EventUtils.synthesizeMouseAtCenter(headerButtons[1], {}, win);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["orange", "south america", "", "", ""],
["red", "north america", "", "", ""],
@ -233,6 +235,7 @@ add_task(async function () {
checkPersistedValue("sortDirection", "descending");
EventUtils.synthesizeMouseAtCenter(headerButtons[0], {}, win);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["blue", "asia", "", "", ""],
["green", "australia", "", "", ""],
@ -247,6 +250,7 @@ add_task(async function () {
checkPersistedValue("sortDirection", "ascending");
EventUtils.synthesizeMouseAtCenter(headerButtons[0], {}, win);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["yellow", "antarctica", "", "", ""],
["violet", "africa", "", "", ""],
@ -261,6 +265,7 @@ add_task(async function () {
checkPersistedValue("sortDirection", "descending");
EventUtils.synthesizeMouseAtCenter(headerButtons[0], {}, win);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["blue", "asia", "", "", ""],
["green", "australia", "", "", ""],
@ -312,6 +317,7 @@ add_task(async function () {
"columns",
"colour,continent,sin:hidden,wonder,dwarf:hidden"
);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["blue", "asia", "", "temple of artemis", ""],
["green", "australia", "", "mausoleum of halicarnassus", ""],
@ -394,6 +400,7 @@ add_task(async function () {
checkHeaderLabels(["continent", "colour", "sin", "wonder", "dwarf"]);
checkHeaderVisibility([true, 150, false, true, false]);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["asia", "blue", "", "temple of artemis", ""],
["australia", "green", "", "mausoleum of halicarnassus", ""],
@ -424,6 +431,7 @@ add_task(async function () {
checkHeaderLabels(["colour", "continent", "sin", "wonder", "dwarf"]);
checkHeaderVisibility([150, true, false, true, false]);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["blue", "asia", "", "temple of artemis", ""],
["green", "australia", "", "mausoleum of halicarnassus", ""],
@ -442,6 +450,7 @@ add_task(async function () {
checkHeaderLabels(["colour", "sin", "wonder", "continent", "dwarf"]);
checkHeaderVisibility([150, false, true, true, false]);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["blue", "", "temple of artemis", "asia", ""],
["green", "", "mausoleum of halicarnassus", "australia", ""],
@ -463,6 +472,7 @@ add_task(async function () {
);
checkHeaderLabels(["colour", "sin", "wonder", "continent", "dwarf"]);
checkHeaderVisibility([150, false, true, false, false]);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["blue", "", "temple of artemis", "", ""],
["green", "", "mausoleum of halicarnassus", "", ""],
@ -484,6 +494,7 @@ add_task(async function () {
); // Have the continents drifted?
checkHeaderLabels(["colour", "sin", "wonder", "continent", "dwarf"]);
checkHeaderVisibility([150, false, true, true, false]);
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["blue", "", "temple of artemis", "asia", ""],
["green", "", "mausoleum of halicarnassus", "australia", ""],
@ -609,6 +620,7 @@ add_task(async function () {
checkHeaderVisibility([150, true, false, false, false]);
checkPersistedValue("sortColumn", "colour");
checkPersistedValue("sortDirection", "ascending");
await new Promise(resolve => win.requestAnimationFrame(resolve));
checkTableRows([
["blue", "asia", "", "", ""],
["green", "australia", "", "", ""],
@ -640,6 +652,9 @@ add_task(async function () {
Services.xulStore.setValue(url, "autoTree", "sortDirection", "descending");
tab.browser.reload();
await BrowserTestUtils.browserLoaded(tab.browser);
await new Promise(resolve =>
tab.browser.contentWindow.requestAnimationFrame(resolve)
);
win = tab.browser.contentWindow;
doc = tab.browser.contentDocument;

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

@ -816,6 +816,8 @@ async function subtestRowCountChange() {
expectedCount * ROW_HEIGHT,
"space for all rows is allocated"
);
await new Promise(resolve => content.requestAnimationFrame(resolve));
}
async function removeValues(index, count, expectedRemoved) {
@ -841,12 +843,15 @@ async function subtestRowCountChange() {
expectedCount * ROW_HEIGHT,
"space for all rows is allocated"
);
await new Promise(resolve => content.requestAnimationFrame(resolve));
}
async function scrollListToPosition(topOfScroll) {
await doListActionAndWaitForRowBuffer(() => {
list.scrollTo(0, topOfScroll);
});
await new Promise(resolve => content.requestAnimationFrame(resolve));
}
Assert.equal(
@ -1251,6 +1256,8 @@ async function subtestExpandCollapse() {
},
};
await new Promise(resolve => content.requestAnimationFrame(resolve));
Assert.equal(
list.querySelectorAll("collapsed").length,
0,
@ -1308,12 +1315,13 @@ async function subtestExpandCollapse() {
// Click the twisties of rows without children.
function performChange(id, expectedChange, changeCallback) {
async function performChange(id, expectedChange, changeCallback) {
listener.reset();
let row = doc.getElementById(id);
const before = row.classList.contains("collapsed");
changeCallback(row);
await new Promise(resolve => content.requestAnimationFrame(resolve));
row = doc.getElementById(id);
if (expectedChange == "collapsed") {
@ -1346,9 +1354,9 @@ async function subtestExpandCollapse() {
}
}
function clickTwisty(id, expectedChange) {
async function clickTwisty(id, expectedChange) {
info(`clicking the twisty on ${id}`);
performChange(id, expectedChange, row =>
await performChange(id, expectedChange, row =>
EventUtils.synthesizeMouseAtCenter(
row.querySelector(".twisty"),
{},
@ -1357,9 +1365,9 @@ async function subtestExpandCollapse() {
);
}
function clickThread(id, expectedChange) {
async function clickThread(id, expectedChange) {
info(`clicking the thread on ${id}`);
performChange(id, expectedChange, row => {
await performChange(id, expectedChange, row => {
EventUtils.synthesizeMouseAtCenter(
row.querySelector(".tree-button-thread"),
{},
@ -1369,7 +1377,7 @@ async function subtestExpandCollapse() {
}
for (const id of idsWithoutChildren) {
clickTwisty(id, null);
await clickTwisty(id, null);
Assert.equal(list.querySelector(".selected").id, id);
}
@ -1403,57 +1411,57 @@ async function subtestExpandCollapse() {
// Collapse row 2.
clickTwisty("row-2", "collapsed");
await clickTwisty("row-2", "collapsed");
checkRowsAreHidden("row-2-1", "row-2-2");
checkSelectedAndCurrent(5, "row-3-1-2");
// Collapse row 3.
clickTwisty("row-3", "collapsed");
await clickTwisty("row-3", "collapsed");
checkRowsAreHidden("row-2-1", "row-2-2", "row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(2, "row-3");
// Expand row 2.
clickTwisty("row-2", "expanded");
await clickTwisty("row-2", "expanded");
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(4, "row-3");
// Expand row 3.
clickTwisty("row-3", "expanded");
await clickTwisty("row-3", "expanded");
checkRowsAreHidden();
checkSelectedAndCurrent(4, "row-3");
// Collapse row 3-1.
clickTwisty("row-3-1", "collapsed");
await clickTwisty("row-3-1", "collapsed");
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(4, "row-3");
// Collapse row 3.
clickTwisty("row-3", "collapsed");
await clickTwisty("row-3", "collapsed");
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(4, "row-3");
// Expand row 3.
clickTwisty("row-3", "expanded");
await clickTwisty("row-3", "expanded");
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(4, "row-3");
// Expand row 3-1.
clickTwisty("row-3-1", "expanded");
await clickTwisty("row-3-1", "expanded");
checkRowsAreHidden();
checkSelectedAndCurrent(4, "row-3");
// Test key presses.
function pressKey(id, key, expectedChange) {
async function pressKey(id, key, expectedChange) {
info(`pressing ${key}`);
performChange(id, expectedChange, () => {
await performChange(id, expectedChange, () => {
EventUtils.synthesizeKey(key, {}, content);
});
}
@ -1461,102 +1469,102 @@ async function subtestExpandCollapse() {
// Row 0 has no children or parent, nothing should happen.
list.selectedIndex = 0;
pressKey("row-1", "VK_LEFT");
await pressKey("row-1", "VK_LEFT");
checkSelectedAndCurrent(0, "row-1");
pressKey("row-1", "VK_RIGHT");
await pressKey("row-1", "VK_RIGHT");
checkSelectedAndCurrent(0, "row-1");
// Collapse row 2.
list.selectedIndex = 1;
pressKey("row-2", "VK_LEFT", "collapsed");
await pressKey("row-2", "VK_LEFT", "collapsed");
checkRowsAreHidden("row-2-1", "row-2-2");
checkSelectedAndCurrent(1, "row-2");
pressKey("row-2", "VK_LEFT");
await pressKey("row-2", "VK_LEFT");
checkRowsAreHidden("row-2-1", "row-2-2");
checkSelectedAndCurrent(1, "row-2");
// Collapse row 3.
list.selectedIndex = 2;
pressKey("row-3", "VK_LEFT", "collapsed");
await pressKey("row-3", "VK_LEFT", "collapsed");
checkRowsAreHidden("row-2-1", "row-2-2", "row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(2, "row-3");
pressKey("row-3", "VK_LEFT");
await pressKey("row-3", "VK_LEFT");
checkRowsAreHidden("row-2-1", "row-2-2", "row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(2, "row-3");
// Expand row 2.
list.selectedIndex = 1;
pressKey("row-2", "VK_RIGHT", "expanded");
await pressKey("row-2", "VK_RIGHT", "expanded");
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(1, "row-2");
// Expand row 3.
list.selectedIndex = 4;
pressKey("row-3", "VK_RIGHT", "expanded");
await pressKey("row-3", "VK_RIGHT", "expanded");
checkRowsAreHidden();
checkSelectedAndCurrent(4, "row-3");
// Go down the tree to row 3-1-1.
pressKey("row-3", "VK_RIGHT");
await pressKey("row-3", "VK_RIGHT");
checkRowsAreHidden();
checkSelectedAndCurrent(5, "row-3-1");
pressKey("row-3", "VK_RIGHT");
await pressKey("row-3", "VK_RIGHT");
checkRowsAreHidden();
checkSelectedAndCurrent(6, "row-3-1-1");
pressKey("row-3-1-1", "VK_RIGHT");
await pressKey("row-3-1-1", "VK_RIGHT");
checkRowsAreHidden();
checkSelectedAndCurrent(6, "row-3-1-1");
// Collapse row 3-1.
pressKey("row-3-1-1", "VK_LEFT");
await pressKey("row-3-1-1", "VK_LEFT");
checkRowsAreHidden();
checkSelectedAndCurrent(5, "row-3-1");
pressKey("row-3-1", "VK_LEFT", "collapsed");
await pressKey("row-3-1", "VK_LEFT", "collapsed");
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(5, "row-3-1");
// Collapse row 3.
pressKey("row-3-1", "VK_LEFT");
await pressKey("row-3-1", "VK_LEFT");
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(4, "row-3");
pressKey("row-3", "VK_LEFT", "collapsed");
await pressKey("row-3", "VK_LEFT", "collapsed");
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(4, "row-3");
// Expand row 3.
pressKey("row-3", "VK_RIGHT", "expanded");
await pressKey("row-3", "VK_RIGHT", "expanded");
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(4, "row-3");
pressKey("row-3", "VK_RIGHT");
await pressKey("row-3", "VK_RIGHT");
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(5, "row-3-1");
// Expand row 3-1.
pressKey("row-3-1", "VK_RIGHT", "expanded");
await pressKey("row-3-1", "VK_RIGHT", "expanded");
checkRowsAreHidden();
checkSelectedAndCurrent(5, "row-3-1");
pressKey("row-3-1", "VK_RIGHT");
await pressKey("row-3-1", "VK_RIGHT");
checkRowsAreHidden();
checkSelectedAndCurrent(6, "row-3-1-1");
pressKey("row-3-1-1", "VK_RIGHT");
await pressKey("row-3-1-1", "VK_RIGHT");
checkRowsAreHidden();
checkSelectedAndCurrent(6, "row-3-1-1");
@ -1568,102 +1576,102 @@ async function subtestExpandCollapse() {
// Row 0 has no children or parent, nothing should happen.
list.selectedIndex = 0;
pressKey("row-1", "VK_RIGHT");
await pressKey("row-1", "VK_RIGHT");
checkSelectedAndCurrent(0, "row-1");
pressKey("row-1", "VK_LEFT");
await pressKey("row-1", "VK_LEFT");
checkSelectedAndCurrent(0, "row-1");
// Collapse row 2.
list.selectedIndex = 1;
pressKey("row-2", "VK_RIGHT", "collapsed");
await pressKey("row-2", "VK_RIGHT", "collapsed");
checkRowsAreHidden("row-2-1", "row-2-2");
checkSelectedAndCurrent(1, "row-2");
pressKey("row-2", "VK_RIGHT");
await pressKey("row-2", "VK_RIGHT");
checkRowsAreHidden("row-2-1", "row-2-2");
checkSelectedAndCurrent(1, "row-2");
// Collapse row 3.
list.selectedIndex = 2;
pressKey("row-3", "VK_RIGHT", "collapsed");
await pressKey("row-3", "VK_RIGHT", "collapsed");
checkRowsAreHidden("row-2-1", "row-2-2", "row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(2, "row-3");
pressKey("row-3", "VK_RIGHT");
await pressKey("row-3", "VK_RIGHT");
checkRowsAreHidden("row-2-1", "row-2-2", "row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(2, "row-3");
// Expand row 2.
list.selectedIndex = 1;
pressKey("row-2", "VK_LEFT", "expanded");
await pressKey("row-2", "VK_LEFT", "expanded");
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(1, "row-2");
// Expand row 3.
list.selectedIndex = 4;
pressKey("row-3", "VK_LEFT", "expanded");
await pressKey("row-3", "VK_LEFT", "expanded");
checkRowsAreHidden();
checkSelectedAndCurrent(4, "row-3");
// Go down the tree to row 3-1-1.
pressKey("row-3", "VK_LEFT");
await pressKey("row-3", "VK_LEFT");
checkRowsAreHidden();
checkSelectedAndCurrent(5, "row-3-1");
pressKey("row-3", "VK_LEFT");
await pressKey("row-3", "VK_LEFT");
checkRowsAreHidden();
checkSelectedAndCurrent(6, "row-3-1-1");
pressKey("row-3-1-1", "VK_LEFT");
await pressKey("row-3-1-1", "VK_LEFT");
checkRowsAreHidden();
checkSelectedAndCurrent(6, "row-3-1-1");
// Collapse row 3-1.
pressKey("row-3-1-1", "VK_RIGHT");
await pressKey("row-3-1-1", "VK_RIGHT");
checkRowsAreHidden();
checkSelectedAndCurrent(5, "row-3-1");
pressKey("row-3-1", "VK_RIGHT", "collapsed");
await pressKey("row-3-1", "VK_RIGHT", "collapsed");
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(5, "row-3-1");
// Collapse row 3.
pressKey("row-3-1", "VK_RIGHT");
await pressKey("row-3-1", "VK_RIGHT");
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(4, "row-3");
pressKey("row-3", "VK_RIGHT", "collapsed");
await pressKey("row-3", "VK_RIGHT", "collapsed");
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(4, "row-3");
// Expand row 3.
pressKey("row-3", "VK_LEFT", "expanded");
await pressKey("row-3", "VK_LEFT", "expanded");
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(4, "row-3");
pressKey("row-3", "VK_LEFT");
await pressKey("row-3", "VK_LEFT");
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
checkSelectedAndCurrent(5, "row-3-1");
// Expand row 3-1.
pressKey("row-3-1", "VK_LEFT", "expanded");
await pressKey("row-3-1", "VK_LEFT", "expanded");
checkRowsAreHidden();
checkSelectedAndCurrent(5, "row-3-1");
pressKey("row-3-1", "VK_LEFT");
await pressKey("row-3-1", "VK_LEFT");
checkRowsAreHidden();
checkSelectedAndCurrent(6, "row-3-1-1");
pressKey("row-3-1-1", "VK_LEFT");
await pressKey("row-3-1-1", "VK_LEFT");
checkRowsAreHidden();
checkSelectedAndCurrent(6, "row-3-1-1");
@ -1674,24 +1682,29 @@ async function subtestExpandCollapse() {
listener.reset();
list.collapseRowAtIndex(6); // No children, no effect.
await new Promise(resolve => content.requestAnimationFrame(resolve));
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
Assert.ok(!listener.collapsedIndex, "'collapsed' event did not fire");
list.expandRowAtIndex(6); // No children, no effect.
await new Promise(resolve => content.requestAnimationFrame(resolve));
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
Assert.ok(!listener.expandedIndex, "'expanded' event did not fire");
list.collapseRowAtIndex(1); // Item with children that aren't selected.
await new Promise(resolve => content.requestAnimationFrame(resolve));
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
Assert.equal(listener.collapsedIndex, 1, "row-2 fired 'collapsed' event");
listener.reset();
list.expandRowAtIndex(1); // Item with children that aren't selected.
await new Promise(resolve => content.requestAnimationFrame(resolve));
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
Assert.equal(listener.expandedIndex, 1, "row-2 fired 'expanded' event");
listener.reset();
list.collapseRowAtIndex(5); // Item with children that are selected.
await new Promise(resolve => content.requestAnimationFrame(resolve));
Assert.ok(selectHandler.seenEvent, "'select' event fired");
Assert.equal(
selectHandler.selectedAtEvent,
@ -1705,6 +1718,7 @@ async function subtestExpandCollapse() {
listener.reset();
list.expandRowAtIndex(5); // Selected item with children.
await new Promise(resolve => content.requestAnimationFrame(resolve));
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
Assert.equal(listener.expandedIndex, 5, "row-3-1 fired 'expanded' event");
checkRowsAreHidden();
@ -1715,6 +1729,7 @@ async function subtestExpandCollapse() {
selectHandler.reset();
list.collapseRowAtIndex(4); // Item with grandchildren that are selected.
await new Promise(resolve => content.requestAnimationFrame(resolve));
Assert.ok(selectHandler.seenEvent, "'select' event fired");
Assert.equal(
selectHandler.selectedAtEvent,
@ -1728,6 +1743,7 @@ async function subtestExpandCollapse() {
listener.reset();
list.expandRowAtIndex(4); // Selected item with grandchildren.
await new Promise(resolve => content.requestAnimationFrame(resolve));
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
Assert.equal(listener.expandedIndex, 4, "row-3 fired 'expanded' event");
checkRowsAreHidden();
@ -1736,7 +1752,7 @@ async function subtestExpandCollapse() {
// Click thread for already expanded thread. Should select all in thread.
selectHandler.reset();
clickThread("row-3"); // Item with grandchildren.
await clickThread("row-3"); // Item with grandchildren.
Assert.ok(selectHandler.seenEvent, "'select' event fired");
Assert.equal(
selectHandler.selectedAtEvent,
@ -1750,9 +1766,10 @@ async function subtestExpandCollapse() {
// Click thread for collapsed thread. Should expand the thread and select all
// children.
list.collapseRowAtIndex(1); // Item with children that aren't selected.
await new Promise(resolve => content.requestAnimationFrame(resolve));
Assert.equal(listener.collapsedIndex, 1, "row-2 fired 'collapsed' event");
checkRowsAreHidden("row-2-1", "row-2-2");
clickThread("row-2", "expanded");
await clickThread("row-2", "expanded");
Assert.equal(listener.expandedIndex, 1, "row-2 fired 'expanded' event");
checkMultiSelect("row-2", "row-2-1", "row-2-2");
checkCurrent(1);
@ -1768,7 +1785,7 @@ async function subtestExpandCollapse() {
EventUtils.synthesizeKey("VK_DOWN", { shiftKey: true }, content);
checkMultiSelect("row-2", "row-2-1", "row-2-2");
checkCurrent(3);
clickTwisty("row-2", "collapsed");
await clickTwisty("row-2", "collapsed");
checkSelectedAndCurrent(1, "row-2");
list.removeEventListener("collapsed", listener);
@ -1822,12 +1839,14 @@ async function subtestScrollWhenExpandCollapse() {
// Click the twisties of rows without children.
function performChange(id, expectedChange, changeCallback) {
async function performChange(id, expectedChange, changeCallback) {
listener.reset();
await new Promise(resolve => content.requestAnimationFrame(resolve));
let row = doc.getElementById(id);
const before = row.classList.contains("collapsed");
changeCallback(row);
await new Promise(resolve => content.requestAnimationFrame(resolve));
row = doc.getElementById(id);
if (expectedChange == "collapsed") {
@ -1860,9 +1879,9 @@ async function subtestScrollWhenExpandCollapse() {
}
}
function clickTwisty(id, expectedChange) {
async function clickTwisty(id, expectedChange) {
info(`clicking the twisty on ${id}`);
performChange(id, expectedChange, row =>
await performChange(id, expectedChange, row =>
EventUtils.synthesizeMouseAtCenter(
row.querySelector(".twisty"),
{},
@ -1873,9 +1892,9 @@ async function subtestScrollWhenExpandCollapse() {
// Test key presses.
function pressKey(id, key, expectedChange) {
async function pressKey(id, key, expectedChange) {
info(`pressing ${key}`);
performChange(id, expectedChange, () => {
await performChange(id, expectedChange, () => {
EventUtils.synthesizeKey(key, {}, content);
});
}
@ -1917,19 +1936,19 @@ async function subtestScrollWhenExpandCollapse() {
list.selectedIndex = 0;
await new Promise(resolve => content.requestAnimationFrame(resolve));
clickTwisty("row-3", "expanded");
await clickTwisty("row-3", "expanded");
await checkFirstVisibleRow(0);
clickTwisty("row-3", "collapsed");
await clickTwisty("row-3", "collapsed");
await checkFirstVisibleRow(0);
list.selectedIndex = 2;
await new Promise(resolve => content.requestAnimationFrame(resolve));
pressKey("row-3", "VK_RIGHT", "expanded");
await pressKey("row-3", "VK_RIGHT", "expanded");
await checkFirstVisibleRow(0);
pressKey("row-3", "VK_LEFT", "collapsed");
await pressKey("row-3", "VK_LEFT", "collapsed");
await checkFirstVisibleRow(0);
// Expanding/collapsing row with many children not all fitting the screen
@ -1938,32 +1957,32 @@ async function subtestScrollWhenExpandCollapse() {
list.selectedIndex = 0;
await new Promise(resolve => content.requestAnimationFrame(resolve));
clickTwisty("row-4", "expanded");
await clickTwisty("row-4", "expanded");
await checkFirstVisibleRow(3);
clickTwisty("row-4", "collapsed");
await clickTwisty("row-4", "collapsed");
await checkVisibleRow(3);
list.selectedIndex = 3;
await new Promise(resolve => content.requestAnimationFrame(resolve));
pressKey("row-4", "VK_RIGHT", "expanded");
await pressKey("row-4", "VK_RIGHT", "expanded");
await checkFirstVisibleRow(3);
pressKey("row-4", "VK_LEFT", "collapsed");
await pressKey("row-4", "VK_LEFT", "collapsed");
await checkVisibleRow(3);
// Expanding a row near the bottom with few children should scroll just the
// last child into view.
pressKey("row-4", "VK_RIGHT", "expanded");
await pressKey("row-4", "VK_RIGHT", "expanded");
list.selectedIndex = 38;
await new Promise(resolve => content.requestAnimationFrame(resolve));
clickTwisty("row-9", "expanded");
await clickTwisty("row-9", "expanded");
await checkLastVisibleRow(43);
clickTwisty("row-9", "collapsed");
await clickTwisty("row-9", "collapsed");
await checkVisibleRow(38);
list.selectedIndex = 0;
@ -1971,10 +1990,10 @@ async function subtestScrollWhenExpandCollapse() {
list.selectedIndex = 38;
await new Promise(resolve => content.requestAnimationFrame(resolve));
pressKey("row-9", "VK_RIGHT", "expanded");
await pressKey("row-9", "VK_RIGHT", "expanded");
checkLastVisibleRow(43);
pressKey("row-9", "VK_LEFT", "collapsed");
await pressKey("row-9", "VK_LEFT", "collapsed");
checkVisibleRow(38);
list.removeEventListener("collapsed", listener);

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

@ -28,15 +28,12 @@ class TestCardRow extends customElements.get("tree-view-table-row") {
this.d3.classList.add("d3");
}
get index() {
return super.index;
}
_fillRow() {
super._fillRow();
set index(index) {
super.index = index;
this.d2.textContent = this.view.getCellText(index, "GeneratedName");
this.d3.textContent = this.view.getCellText(index, "PrimaryEmail");
this.dataset.value = this.view.values[index];
this.d2.textContent = this.view.getCellText(this._index, "GeneratedName");
this.d3.textContent = this.view.getCellText(this._index, "PrimaryEmail");
this.dataset.value = this.view.values[this._index];
}
}
customElements.define("test-row", TestCardRow, { extends: "tr" });
@ -54,13 +51,10 @@ class AlternativeCardRow extends customElements.get("tree-view-table-row") {
this.cell = this.appendChild(document.createElement("td"));
}
get index() {
return super.index;
}
_fillRow() {
super._fillRow();
set index(index) {
super.index = index;
this.cell.textContent = this.view.getCellText(index, "GeneratedName");
this.cell.textContent = this.view.getCellText(this._index, "GeneratedName");
}
}
customElements.define("alternative-row", AlternativeCardRow, {

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

@ -10,12 +10,6 @@
<title>Test for the tree-view custom element</title>
<link rel="stylesheet" href="chrome://messenger/skin/shared/tree-listbox.css" />
<style>
:root {
--color-gray-20: gray;
--selected-item-color: rebeccapurple;
--selected-item-text-color: white;
}
#testTree {
/* We want a total visible row area of 630px, but we need to account for the
* height of the header as well. */
@ -41,6 +35,11 @@
line-height: 1.2;
font-size: 13px;
}
tr[is="test-row"].selected {
background-color: rebeccapurple;
color: white;
}
</style>
<script type="module" src="tree-element-test-header.mjs"></script>
</head>

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

@ -40,16 +40,13 @@ class TestCardRow extends customElements.get("tree-view-table-row") {
this.d2.classList.add("d2");
}
get index() {
return super.index;
}
_fillRow() {
super._fillRow();
set index(index) {
super.index = index;
this.id = this.view.getRowProperties(index);
this.id = this.view.getRowProperties(this._index);
this.classList.remove("level0", "level1", "level2");
this.classList.add(`level${this.view.getLevel(index)}`);
this.d2.textContent = this.view.getCellText(index, "text");
this.classList.add(`level${this.view.getLevel(this._index)}`);
this.d2.textContent = this.view.getCellText(this._index, "text");
}
}
customElements.define("test-row", TestCardRow, { extends: "tr" });

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

@ -5,12 +5,6 @@
<title>Test for the tree-view custom element</title>
<link rel="stylesheet" href="chrome://messenger/skin/shared/tree-listbox.css" />
<style>
:root {
--color-gray-20: gray;
--selected-item-color: rebeccapurple;
--selected-item-text-color: white;
}
.tree-view-scrollable-container {
height: 630px;
scroll-behavior: unset;
@ -54,6 +48,11 @@
tr[is="test-row"].level2 .d2 {
padding-inline-start: 2em;
}
tr[is="test-row"].selected {
background-color: rebeccapurple;
color: white;
}
</style>
<script type="module" src="tree-element-test-levels.mjs"></script>
</head>

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

@ -10,12 +10,6 @@
<title>Test for the tree-view custom element</title>
<link rel="stylesheet" href="chrome://messenger/skin/shared/tree-listbox.css" />
<style>
:root {
--color-gray-20: gray;
--selected-item-color: rebeccapurple;
--selected-item-text-color: white;
}
/* We want a total visible row area of 630px. Intentionally avoid leaving
* room for a header. */
#testTree {
@ -41,6 +35,11 @@
line-height: 1.2;
font-size: 13px;
}
tr[is="test-row"].selected {
background-color: rebeccapurple;
color: white;
}
</style>
<script type="module" src="tree-element-test-no-header.mjs"></script>
</head>

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

@ -40,16 +40,13 @@ class TestCardRow extends customElements.get("tree-view-table-row") {
this.d2.classList.add("d2");
}
get index() {
return super.index;
}
_fillRow() {
super._fillRow();
set index(index) {
super.index = index;
this.id = this.view.getRowProperties(index);
this.id = this.view.getRowProperties(this._index);
this.classList.remove("level0", "level1", "level2");
this.classList.add(`level${this.view.getLevel(index)}`);
this.d2.textContent = this.view.getCellText(index, "text");
this.classList.add(`level${this.view.getLevel(this._index)}`);
this.d2.textContent = this.view.getCellText(this._index, "text");
}
}
customElements.define("test-row", TestCardRow, { extends: "tr" });

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

@ -5,12 +5,6 @@
<title>Test for the tree-view custom element</title>
<link rel="stylesheet" href="chrome://messenger/skin/shared/tree-listbox.css" />
<style>
:root {
--color-gray-20: gray;
--selected-item-color: rebeccapurple;
--selected-item-text-color: white;
}
.tree-view-scrollable-container {
height: 630px;
scroll-behavior: unset;
@ -54,6 +48,11 @@
tr[is="test-row"].level2 .d2 {
padding-inline-start: 2em;
}
tr[is="test-row"].selected {
background-color: rebeccapurple;
color: white;
}
</style>
<script type="module" src="tree-element-test-scroll.mjs"></script>
</head>

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

@ -1218,21 +1218,20 @@ customElements.whenDefined("tree-view-table-row").then(() => {
this.appendChild(this.cell);
}
get index() {
return super.index;
}
/**
* Override the row setter to generate the layout.
* Generate the layout for the current card.
*
* @note This element could be recycled, make sure you set or clear all
* properties.
*/
set index(index) {
super.index = index;
_fillRow() {
super._fillRow();
const card = this.view.getCardFromRow(index);
this.name.textContent = this.view.getCellText(index, "GeneratedName");
const card = this.view.getCardFromRow(this._index);
this.name.textContent = this.view.getCellText(
this._index,
"GeneratedName"
);
// Add the address book name for All Address Books if in the sort Context
// Address Book is checked. This is done for the list view only.
@ -1249,7 +1248,10 @@ customElements.whenDefined("tree-view-table-row").then(() => {
addressBookName.classList.add("address-book-name");
this.firstLine.appendChild(addressBookName);
}
addressBookName.textContent = this.view.getCellText(index, "addrbook");
addressBookName.textContent = this.view.getCellText(
this._index,
"addrbook"
);
} else {
this.querySelector(".address-book-name")?.remove();
}
@ -1310,26 +1312,22 @@ customElements.whenDefined("tree-view-table-row").then(() => {
}
}
get index() {
return super.index;
}
/**
* Override the row setter to generate the layout.
* Generate the layout for the current card.
*
* @note This element could be recycled, make sure you set or clear all
* properties.
*/
set index(index) {
super.index = index;
_fillRow() {
super._fillRow();
const card = this.view.getCardFromRow(index);
const card = this.view.getCardFromRow(this._index);
this.classList.toggle("MailList", card.isMailList);
for (const column of cardsPane.COLUMNS) {
const cell = this.querySelector(`.${column.id.toLowerCase()}-column`);
if (!column.hidden) {
cell.textContent = this.view.getCellText(index, column.id);
cell.textContent = this.view.getCellText(this._index, column.id);
continue;
}

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

@ -1154,6 +1154,7 @@ add_task(async function test_list_table_layout() {
await TestUtils.waitForCondition(
() => !cardsHeader.querySelector(`[id="addrbook"]`).hidden
);
await new Promise(resolve => abWindow.requestAnimationFrame(resolve));
// Check for the contact that the column is shown.
Assert.ok(
@ -1217,6 +1218,7 @@ add_task(async function test_list_all_address_book() {
await TestUtils.waitForCondition(
() => !cardsHeader.querySelector(`[id="addrbook"]`).hidden
);
await new Promise(resolve => abWindow.requestAnimationFrame(resolve));
Assert.ok(
cardsList

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

@ -73,6 +73,7 @@ async function waitForCardsListReady(list) {
const eventName = "_treerowbufferfillAbListReady";
list._rowBufferReadyEvent = new CustomEvent(eventName);
await BrowserTestUtils.waitForEvent(list, eventName);
await new Promise(resolve => list.ownerGlobal.requestAnimationFrame(resolve));
}
async function openAddressBookWindow() {

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

@ -710,25 +710,24 @@ add_task(async function test_custom_column_invalidation() {
assert_visible_columns(INBOX_DEFAULTS);
const about3Pane = document.getElementById("tabmail").currentAbout3Pane;
let factor = 1;
ThreadPaneColumns.addCustomColumn("testCol1", {
name: "Test1",
hidden: true,
sortCallback(header) {
return header.subject.length * factor;
return header.subject.length;
},
textCallback(header) {
return header.subject.length * factor;
return header.subject.length;
},
});
ThreadPaneColumns.addCustomColumn("testCol2", {
name: "Test2",
hidden: true,
sortCallback(header) {
return header.subject.length * factor;
return header.subject.length * 2;
},
textCallback(header) {
return header.subject.length * factor;
return header.subject.length * 2;
},
});
await new Promise(setTimeout);
@ -749,34 +748,10 @@ add_task(async function test_custom_column_invalidation() {
10
);
Assert.greater(value1, 0, "Content of custom cell #1 should be non-zero");
Assert.greater(value2, 0, "Content of custom cell #2 should be non-zero");
Assert.equal(
value1,
value2,
"Content of both custom cells should be identical"
);
factor = 2;
ThreadPaneColumns.refreshCustomColumn("testCol1");
await new Promise(setTimeout);
const refreshedValue1 = parseInt(
row.querySelector(".testcol1-column").textContent,
10
);
const refreshedValue2 = parseInt(
row.querySelector(".testcol2-column").textContent,
10
);
Assert.equal(
refreshedValue1,
value1 * 2,
"Content of custom cell #1 should have doubled"
);
Assert.equal(
refreshedValue2,
value2,
"Content of custom cell #2 should have not changed"
2 * value1,
"Content of custom cell #2 should be twice cell #1"
);
ThreadPaneColumns.removeCustomColumn("testCol1");

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

@ -30,8 +30,6 @@ var {
"resource://testing-common/mail/FolderDisplayHelpers.sys.mjs"
);
const onHdrDeletedTimeout = 500;
var singleFolder, folderA, folderB, multiFolder;
var tab1, tab2;
var about3Pane;
@ -58,16 +56,15 @@ add_setup(async function () {
const assertReplyCount = async replyCount => {
await switch_tab();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve =>
about3Pane.setTimeout(resolve, onHdrDeletedTimeout)
);
Assert.equal(
about3Pane.threadTree.getElementsByClassName("thread-replies")[0]
.textContent,
`${replyCount} replies`,
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
const replies = about3Pane.threadTree.querySelector(".thread-replies");
Assert.deepEqual(
about3Pane.document.l10n.getAttributes(replies),
{ id: "threadpane-replies", args: { count: replyCount } },
"Thread header in background tab should show the correct message count."
);
await switch_tab();
};
@ -141,16 +138,21 @@ add_task(async function delete_from_expanded_xfthread() {
const assertMessagesCount = async (unreadCount, messagesCount) => {
await switch_tab();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve =>
about3Pane.setTimeout(resolve, onHdrDeletedTimeout)
);
Assert.equal(
about3Pane.threadTree.getElementsByClassName("sort-header-details")[0]
.textContent,
`${unreadCount} unread of ${messagesCount} messages`,
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
const count = about3Pane.threadTree.querySelector(".sort-header-details");
Assert.deepEqual(
about3Pane.document.l10n.getAttributes(count),
{
id: "threadpane-sort-header-unread-count",
args: {
unread: unreadCount,
total: messagesCount,
},
},
"Group header in background tab should show the correct message count."
);
await switch_tab();
};