--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -239,16 +239,18 @@ if (AppConstants.MOZ_UPDATER) {
]);
}
}
// A promise that resolves when the list of application handlers is loaded.
// We store this in a global so tests can await it.
var promiseLoadHandlersList;
+var gNodeToObjectMap = new WeakMap();
+
var gMainPane = {
// The set of types the app knows how to handle. A hash of HandlerInfoWrapper
// objects, indexed by type.
_handledTypes: {},
// The list of types we can show, sorted by the sort column/direction.
// An array of HandlerInfoWrapper objects. We build this list when we first
// load the data and then rebuild it when users change a pref that affects
@@ -1500,44 +1502,34 @@ var gMainPane = {
handlerInfo.disambiguateDescription = true;
otherHandlerInfo.disambiguateDescription = true;
}
}
},
_rebuildView() {
let lastSelectedType = this._list.selectedItem &&
- this._list.selectedItem.getAttribute("type");
+ HandlerListItem.forNode(this._list.selectedItem).handlerInfoWrapper.type;
// Clear the list of entries.
while (this._list.childNodes.length > 1)
this._list.removeChild(this._list.lastChild);
var visibleTypes = this._visibleTypes;
// If the user is filtering the list, then only show matching types.
if (this._filter.value)
visibleTypes = visibleTypes.filter(this._matchesFilter, this);
for (let visibleType of visibleTypes) {
- let item = document.createElement("richlistitem");
- item.setAttribute("type", visibleType.type);
- item.setAttribute("typeDescription", visibleType.typeDescription);
- if (visibleType.smallIcon)
- item.setAttribute("typeIcon", visibleType.smallIcon);
- item.setAttribute("actionDescription", visibleType.actionDescription);
-
- if (!this._setIconClassForPreferredAction(visibleType, item)) {
- item.setAttribute("actionIcon", visibleType.actionIcon);
- }
-
- this._list.appendChild(item);
+ let item = new HandlerListItem(visibleType);
+ this._list.appendChild(item.node);
if (visibleType.type === lastSelectedType) {
- this._list.selectedItem = item;
+ this._list.selectedItem = item.node;
}
}
},
_matchesFilter(aType) {
var filterValue = this._filter.value.toLowerCase();
return aType.typeDescription.toLowerCase().includes(filterValue) ||
aType.actionDescription.toLowerCase().includes(filterValue);
@@ -1945,40 +1937,34 @@ var gMainPane = {
handlerInfo.store();
// Make sure the handler info object is flagged to indicate that there is
// now some user configuration for the type.
handlerInfo.handledOnlyByPlugin = false;
// Update the action label and image to reflect the new preferred action.
- typeItem.setAttribute("actionDescription", handlerInfo.actionDescription);
- if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
- typeItem.setAttribute("actionIcon", handlerInfo.actionIcon);
- }
+ HandlerListItem.forNode(typeItem).refreshAction();
},
manageApp(aEvent) {
// Don't let the normal "on select action" handler get this event,
// as we handle it specially ourselves.
aEvent.stopPropagation();
var typeItem = this._list.selectedItem;
var handlerInfo = this._handledTypes[typeItem.type];
let onComplete = () => {
// Rebuild the actions menu so that we revert to the previous selection,
// or "Always ask" if the previous default application has been removed
this.rebuildActionsMenu();
// update the richlistitem too. Will be visible when selecting another row
- typeItem.setAttribute("actionDescription", handlerInfo.actionDescription);
- if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
- typeItem.setAttribute("actionIcon", handlerInfo.actionIcon);
- }
+ HandlerListItem.forNode(typeItem).refreshAction();
};
gSubDialog.open("chrome://browser/content/preferences/applicationManager.xul",
"resizable=no", handlerInfo, onComplete);
},
chooseApp(aEvent) {
@@ -2062,31 +2048,16 @@ var gMainPane = {
// Prompt the user to pick an app. If they pick one, and it's a valid
// selection, then add it to the list of possible handlers.
fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
fp.appendFilters(Ci.nsIFilePicker.filterApps);
fp.open(fpCallback);
}
},
- _setIconClassForPreferredAction(aHandlerInfo, aElement) {
- // If this returns true, the attribute that CSS sniffs for was set to something
- // so you shouldn't manually set an icon URI.
- // This removes the existing actionIcon attribute if any, even if returning false.
- aElement.removeAttribute("actionIcon");
-
- if (aHandlerInfo.actionIconClass) {
- aElement.setAttribute(APP_ICON_ATTR_NAME, aHandlerInfo.actionIconClass);
- return true;
- }
-
- aElement.removeAttribute(APP_ICON_ATTR_NAME);
- return false;
- },
-
_getIconURLForHandlerApp(aHandlerApp) {
if (aHandlerApp instanceof Ci.nsILocalHandlerApp)
return this._getIconURLForFile(aHandlerApp.executable);
if (aHandlerApp instanceof Ci.nsIWebHandlerApp)
return this._getIconURLForWebApp(aHandlerApp.uriTemplate);
if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo)
@@ -2450,17 +2421,54 @@ ArrayEnumerator.prototype = {
return this._contents[this._index++];
}
};
function isFeedType(t) {
return t == TYPE_MAYBE_FEED || t == TYPE_MAYBE_VIDEO_FEED || t == TYPE_MAYBE_AUDIO_FEED;
}
-// HandlerInfoWrapper
+/**
+ * This is associated to <richlistitem> elements in the handlers view.
+ */
+class HandlerListItem {
+ static forNode(node) {
+ return gNodeToObjectMap.get(node);
+ }
+
+ constructor(handlerInfoWrapper) {
+ this.handlerInfoWrapper = handlerInfoWrapper;
+ this.node = document.createElement("richlistitem");
+ gNodeToObjectMap.set(this.node, this);
+
+ this.node.setAttribute("type", this.handlerInfoWrapper.type);
+ this.node.setAttribute("typeDescription",
+ this.handlerInfoWrapper.typeDescription);
+ if (this.handlerInfoWrapper.smallIcon) {
+ this.node.setAttribute("typeIcon", this.handlerInfoWrapper.smallIcon);
+ } else {
+ this.node.removeAttribute("typeIcon");
+ }
+
+ this.refreshAction();
+ }
+
+ refreshAction() {
+ this.node.setAttribute("actionDescription",
+ this.handlerInfoWrapper.actionDescription);
+ if (this.handlerInfoWrapper.actionIconClass) {
+ this.node.setAttribute(APP_ICON_ATTR_NAME,
+ this.handlerInfoWrapper.actionIconClass);
+ this.node.removeAttribute("actionIcon");
+ } else {
+ this.node.removeAttribute(APP_ICON_ATTR_NAME);
+ this.node.setAttribute("actionIcon", this.handlerInfoWrapper.actionIcon);
+ }
+ }
+}
/**
* This object wraps nsIHandlerInfo with some additional functionality
* the Applications prefpane needs to display and allow modification of
* the list of handled types.
*
* We create an instance of this wrapper for each entry we might display
* in the prefpane, and we compose the instances from various sources,