Bug 614416: Building add-on lists is too slow. r=Unfocused, a=blocks-final

--HG--
extra : rebase_source : 9daed9f67dbb8fd0dd9130d7cac10dd137d61240
This commit is contained in:
Dave Townsend 2010-11-30 14:18:00 -08:00
parent e2b5cef33a
commit 042c7d0a9c
5 changed files with 136 additions and 82 deletions

View File

@ -74,8 +74,6 @@ const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml
const VIEW_DEFAULT = "addons://list/extension";
const INTEGER_FIELDS = ["dateUpdated", "size", "relevancescore"];
var gStrings = {};
XPCOMUtils.defineLazyServiceGetter(gStrings, "bundleSvc",
"@mozilla.org/intl/stringbundle;1",
@ -1163,16 +1161,81 @@ function createItem(aObj, aIsInstall, aIsRemote) {
// the binding handles the rest
item.setAttribute("value", aObj.id);
// The XUL sort service only supports 32 bit integers so we strip the
// milliseconds to make this small enough
if (aObj.updateDate)
item.setAttribute("dateUpdated", aObj.updateDate.getTime() / 1000);
if (aObj.size)
item.setAttribute("size", aObj.size);
return item;
}
function sortElements(aElements, aSortBy, aAscending) {
const DATE_FIELDS = ["updateDate"];
const INTEGER_FIELDS = ["size", "relevancescore"];
function dateCompare(a, b) {
var aTime = a.getTime();
var bTime = b.getTime();
if (aTime < bTime)
return -1;
if (aTime > bTime)
return 1;
return 0;
}
function intCompare(a, b) {
return a - b;
}
function stringCompare(a, b) {
return a.localeCompare(b);
}
function getValue(aObj) {
if (!aObj)
return null;
if (aObj.hasAttribute(aSortBy))
return aObj.getAttribute(aSortBy);
addon = aObj.mAddon || aObj.mInstall;
if (!addon)
return null;
return addon[aSortBy];
}
var sortFunc = stringCompare;
if (DATE_FIELDS.indexOf(aSortBy) != -1)
sortFunc = dateCompare;
else if (INTEGER_FIELDS.indexOf(aSortBy) != -1)
sortFunc = intCompare;
aElements.sort(function(a, b) {
if (!aAscending)
[a, b] = [b, a];
var aValue = getValue(a);
var bValue = getValue(b);
if (!aValue && !bValue)
return 0;
if (!aValue)
return -1;
if (!bValue)
return 1;
return sortFunc(aValue, bValue);
});
}
function sortList(aList, aSortBy, aAscending) {
var elements = Array.slice(aList.childNodes, 0);
sortElements(elements, aSortBy, aAscending);
while (aList.listChild)
aList.removeChild(aList.lastChild);
elements.forEach(function(aElement) {
aList.appendChild(aElement);
});
}
function getAddonsAndInstalls(aType, aCallback) {
var addonTypes = null, installTypes = null;
if (aType != null) {
@ -1562,8 +1625,9 @@ var gSearchView = {
this._pendingSearches = 2;
this._sorters.setSort("relevancescore", false);
var elements = [];
function createSearchResults(aObjsList, aIsInstall, aIsRemote) {
var createdCount = 0;
aObjsList.forEach(function(aObj) {
let score = 0;
if (aQuery.length > 0) {
@ -1577,16 +1641,18 @@ var gSearchView = {
if (aIsRemote)
gCachedAddons[aObj.id] = aObj;
self._listBox.insertBefore(item, self._listBox.lastChild);
createdCount++;
elements.push(item);
});
return createdCount;
}
function finishSearch(createdCount) {
if (createdCount > 0)
self.onSortChanged(self._sorters.sortBy, self._sorters.ascending);
if (elements.length > 0) {
sortElements(elements, self._sorters.sortBy, self._sorters.ascending);
elements.forEach(function(aElement) {
self._listBox.insertBefore(aElement, self._listBox.lastChild);
});
self.updateListAttributes();
}
self._pendingSearches--;
self.updateView();
@ -1599,9 +1665,9 @@ var gSearchView = {
if (gViewController && aRequest != gViewController.currentViewRequest)
return;
var createdCount = createSearchResults(aAddons, false, false);
createdCount += createSearchResults(aInstalls, true, false);
finishSearch(createdCount);
createSearchResults(aAddons, false, false);
createSearchResults(aInstalls, true, false);
finishSearch();
});
var maxRemoteResults = 0;
@ -1748,18 +1814,7 @@ var gSearchView = {
this._allResultsLink.hidden = false;
},
onSortChanged: function(aSortBy, aAscending) {
var footer = this._listBox.lastChild;
this._listBox.removeChild(footer);
var hints = aAscending ? "ascending" : "descending";
if (INTEGER_FIELDS.indexOf(aSortBy) >= 0)
hints += " integer";
var sortService = Cc["@mozilla.org/xul/xul-sort-service;1"].
getService(Ci.nsIXULSortService);
sortService.sort(this._listBox, aSortBy, hints);
updateListAttributes: function() {
var item = this._listBox.querySelector("richlistitem[remote='true'][first]");
if (item)
item.removeAttribute("first");
@ -1784,6 +1839,15 @@ var gSearchView = {
items[items.length - 1].setAttribute("last", true);
}
},
onSortChanged: function(aSortBy, aAscending) {
var footer = this._listBox.lastChild;
this._listBox.removeChild(footer);
sortList(this._listBox, aSortBy, aAscending);
this.updateListAttributes();
this._listBox.appendChild(footer);
},
@ -1860,20 +1924,22 @@ var gListView = {
if (gViewController && aRequest != gViewController.currentViewRequest)
return;
for (let i = 0; i < aAddonsList.length; i++) {
let item = createItem(aAddonsList[i]);
self._listBox.appendChild(item);
}
var elements = [];
for (let i = 0; i < aInstallsList.length; i++) {
let item = createItem(aInstallsList[i], true);
self._listBox.appendChild(item);
}
for (let i = 0; i < aAddonsList.length; i++)
elements.push(createItem(aAddonsList[i]));
if (self._listBox.childElementCount > 0)
self.onSortChanged(self._sorters.sortBy, self._sorters.ascending);
else
for (let i = 0; i < aInstallsList.length; i++)
elements.push(createItem(aInstallsList[i], true));
if (elements.length > 0) {
sortElements(elements, self._sorters.sortBy, self._sorters.ascending);
elements.forEach(function(aElement) {
self._listBox.appendChild(aElement);
});
} else {
self.showEmptyNotice(true);
}
gEventManager.registerInstallListener(self);
gViewController.updateCommands();
@ -1906,13 +1972,7 @@ var gListView = {
},
onSortChanged: function(aSortBy, aAscending) {
var hints = aAscending ? "ascending" : "descending";
if (INTEGER_FIELDS.indexOf(aSortBy) >= 0)
hints += " integer";
var sortService = Cc["@mozilla.org/xul/xul-sort-service;1"].
getService(Ci.nsIXULSortService);
sortService.sort(this._listBox, aSortBy, hints);
sortList(this._listBox, aSortBy, aAscending);
},
onNewInstall: function(aInstall) {
@ -2402,19 +2462,23 @@ var gUpdatesView = {
if (gViewController && aRequest != gViewController.currentViewRequest)
return;
var elements = [];
let threshold = Date.now() - UPDATES_RECENT_TIMESPAN;
aAddonsList.forEach(function(aAddon) {
if (!aAddon.updateDate || aAddon.updateDate.getTime() < threshold)
return;
let item = createItem(aAddon);
self._listBox.appendChild(item);
elements.push(createItem(aAddon));
});
if (self._listBox.itemCount > 0)
self.onSortChanged(self._sorters.sortBy, self._sorters.ascending);
else
if (elements.length > 0) {
sortElements(elements, self._sorters.sortBy, self._sorters.ascending);
elements.forEach(function(aElement) {
self._listBox.appendChild(aElement);
});
} else {
self.showEmptyNotice(true);
}
gViewController.notifyViewChanged();
});
@ -2440,6 +2504,8 @@ var gUpdatesView = {
self._listBox.removeItemAt(0);
}
var elements = [];
aInstallsList.forEach(function(aInstall) {
if (!self.isManualUpdate(aInstall))
return;
@ -2449,12 +2515,15 @@ var gUpdatesView = {
item.addEventListener("IncludeUpdateChanged", function() {
self.maybeDisableUpdateSelected();
}, false);
self._listBox.appendChild(item);
elements.push(item);
});
if (self._listBox.itemCount > 0) {
if (elements.length > 0) {
self._updateSelected.hidden = false;
self.onSortChanged(self._sorters.sortBy, self._sorters.ascending);
sortElements(elements, self._sorters.sortBy, self._sorters.ascending);
elements.forEach(function(aElement) {
self._listBox.appendChild(aElement);
});
} else {
self.showEmptyNotice(true);
}
@ -2575,13 +2644,7 @@ var gUpdatesView = {
},
onSortChanged: function(aSortBy, aAscending) {
var hints = aAscending ? "ascending" : "descending";
if (INTEGER_FIELDS.indexOf(aSortBy) >= 0)
hints += " integer";
var sortService = Cc["@mozilla.org/xul/xul-sort-service;1"].
getService(Ci.nsIXULSortService);
sortService.sort(this._listBox, aSortBy, hints);
sortList(this._listBox, aSortBy, aAscending);
},
onNewInstall: function(aInstall) {

View File

@ -246,7 +246,7 @@
<xul:button anonid="date-btn" class="sorter"
label="&sort.dateUpdated.label;"
tooltiptext="&sort.dateUpdated.tooltip;"
oncommand="this.parentNode._handleChange('dateUpdated');"/>
oncommand="this.parentNode._handleChange('updateDate');"/>
<xul:button anonid="relevance-btn" class="sorter" hidden="true"
label="&sort.relevance.label;"
tooltiptext="&sort.relevance.tooltip;"
@ -348,7 +348,7 @@
this._btnName.checked = false;
}
if (sortBy == "dateUpdated") {
if (sortBy == "updateDate") {
this._btnDate.checkState = checkState;
this._btnDate.checked = true;
} else {
@ -1034,11 +1034,6 @@
this.setAttribute("name", aAddon.name);
if (aAddon.size)
this.setAttribute("size", aAddon.size);
else
this.removeAttribute("size");
var iconURL = this.mAddon.iconURL;
if (iconURL)
this._icon.src = iconURL;
@ -1111,13 +1106,10 @@
);
}
if (this.mAddon.updateDate) {
if (this.mAddon.updateDate)
this._dateUpdated.value = formatDate(this.mAddon.updateDate);
this.setAttribute("dateUpdated", this.mAddon.updateDate.getTime() / 1000);
} else {
else
this._dateUpdated.value = this._dateUpdated.getAttribute("unknown");
this.removeAttribute("dateUpdated");
}
]]></body>
</method>

View File

@ -416,7 +416,7 @@
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
</hbox>
<spacer flex="1"/>
<hbox id="updates-sorters" class="sort-controls" sortby="dateUpdated"
<hbox id="updates-sorters" class="sort-controls" sortby="updateDate"
ascending="false"/>
</hbox>
<vbox id="updates-list-empty" class="alert-container"

View File

@ -68,7 +68,7 @@ add_test(function() {
add_test(function() {
var updatesList = gManagerWindow.document.getElementById("updates-list");
var items = updatesList.getElementsByTagName("richlistitem");
var possible = ["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon2@tests.mozilla.org"];
var possible = ["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon3@tests.mozilla.org"];
var expected = ["addon2@tests.mozilla.org", "addon1@tests.mozilla.org"];
for (let i = 0; i < items.length; i++) {
let item = items[i];
@ -76,7 +76,7 @@ add_test(function() {
if (possible.indexOf(itemId) == -1)
continue; // skip over any other addons, such as shipped addons that would update on every build
isnot(expected.length, 0, "Should be expecting more items");
is(itemId, expected.shift(), "Should get expected item based on recenty of update");
is(itemId, expected.shift(), "Should get expected item based on recentness of update");
if (itemId == "addon1@tests.mozilla.org")
is_element_visible(item._relNotesToggle, "Release notes toggle should be visible for addon with release notes");
else

View File

@ -60,9 +60,8 @@ function check_order(aExpectedOrder) {
var node = list.firstChild;
while (node) {
var id = node.getAttribute("value");
if (id && id.substring(id.length - 18) != "@tests.mozilla.org")
return;
order.push(node.getAttribute("value"));
if (id && id.substring(id.length - 18) == "@tests.mozilla.org")
order.push(node.getAttribute("value"));
node = node.nextSibling;
}