Bug 1459556 - Part 1 - Remove the implementation from the "handler" binding. r=jaws
MozReview-Commit-ID: IN1C5NC9Rzb
--- a/browser/components/preferences/handlers.xml
+++ b/browser/components/preferences/handlers.xml
@@ -6,39 +6,16 @@
<!-- import-globals-from in-content/main.js -->
<bindings id="handlerBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="handler" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
- <implementation>
- <property name="type" readonly="true">
- <getter>
- return this.getAttribute("type");
- </getter>
- </property>
- <!-- Overriding listitem -->
- <property name="selected" onget="return this.getAttribute('selected') == 'true';">
- <setter><![CDATA[
- if (val) {
- this.setAttribute("selected", "true");
- gMainPane.rebuildActionsMenu();
- } else {
- this.removeAttribute("selected");
- }
-
- document.getAnonymousElementByAttribute(this, "anonid", "selected").setAttribute("hidden", !val);
- document.getAnonymousElementByAttribute(this, "anonid", "not-selected").setAttribute("hidden", !!val);
-
- return val;
- ]]></setter>
- </property>
- </implementation>
<content>
<xul:hbox flex="1" equalsize="always">
<xul:hbox flex="1" align="center" xbl:inherits="tooltiptext=typeDescription">
<xul:image src="moz-icon://goat?size=16" class="typeIcon"
xbl:inherits="src=typeIcon" height="16" width="16"/>
<xul:label flex="1" crop="end" xbl:inherits="value=typeDescription"/>
</xul:hbox>
<xul:hbox anonid="not-selected" flex="1" align="center" xbl:inherits="tooltiptext=actionDescription">
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -578,16 +578,17 @@ var gMainPane = {
preInit() {
promiseLoadHandlersList = new Promise((resolve, reject) => {
// Load the data and build the list of handlers for applications pane.
// By doing this after pageshow, we ensure it doesn't delay painting
// of the preferences page.
window.addEventListener("pageshow", async () => {
try {
+ this._initListEventHandlers();
this._loadData();
await this._rebuildVisibleTypes();
this._sortVisibleTypes();
this._rebuildView();
resolve();
} catch (ex) {
reject(ex);
}
@@ -1443,16 +1444,41 @@ var gMainPane = {
handlerInfoWrapper.handledOnlyByPlugin = false;
}
},
// View Construction
+ selectedHandlerListItem: null,
+
+ _initListEventHandlers() {
+ this._list.addEventListener("select", event => {
+ if (event.target != this._list) {
+ return;
+ }
+
+ let handlerListItem = this._list.selectedItem &&
+ HandlerListItem.forNode(this._list.selectedItem);
+ if (this.selectedHandlerListItem == handlerListItem) {
+ return;
+ }
+
+ if (this.selectedHandlerListItem) {
+ this.selectedHandlerListItem.showActionsMenu = false;
+ }
+ this.selectedHandlerListItem = handlerListItem;
+ if (handlerListItem) {
+ this.rebuildActionsMenu();
+ handlerListItem.showActionsMenu = true;
+ }
+ });
+ },
+
async _rebuildVisibleTypes() {
this._visibleTypes = [];
// Map whose keys are string descriptions and values are references to the
// first visible HandlerInfoWrapper that has this description. We use this
// to determine whether or not to annotate descriptions with their types to
// distinguish duplicate descriptions from each other.
let visibleDescriptions = new Map();
@@ -1501,18 +1527,19 @@ var gMainPane = {
// add the type to the description on both HandlerInfoWrapper objects.
handlerInfo.disambiguateDescription = true;
otherHandlerInfo.disambiguateDescription = true;
}
}
},
_rebuildView() {
- let lastSelectedType = this._list.selectedItem &&
- HandlerListItem.forNode(this._list.selectedItem).handlerInfoWrapper.type;
+ let lastSelectedType = this.selectedHandlerListItem &&
+ this.selectedHandlerListItem.handlerInfoWrapper.type;
+ this.selectedHandlerListItem = null;
// 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.
@@ -1580,17 +1607,17 @@ var gMainPane = {
},
/**
* Rebuild the actions menu for the selected entry. Gets called by
* the richlistitem constructor when an entry in the list gets selected.
*/
rebuildActionsMenu() {
var typeItem = this._list.selectedItem;
- var handlerInfo = this._handledTypes[typeItem.type];
+ var handlerInfo = this.selectedHandlerListItem.handlerInfoWrapper;
var menu =
document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
var menuPopup = menu.menupopup;
// Clear out existing items.
while (menuPopup.hasChildNodes())
menuPopup.removeChild(menuPopup.lastChild);
@@ -1694,17 +1721,17 @@ var gMainPane = {
menuPopup.appendChild(menuItem);
possibleAppMenuItems.push(menuItem);
}
// Add gio handlers
if (Cc["@mozilla.org/gio-service;1"]) {
let gIOSvc = Cc["@mozilla.org/gio-service;1"].
getService(Ci.nsIGIOService);
- var gioApps = gIOSvc.getAppsForURIScheme(typeItem.type);
+ var gioApps = gIOSvc.getAppsForURIScheme(handlerInfo.type);
let enumerator = gioApps.enumerate();
let possibleHandlers = handlerInfo.possibleApplicationHandlers;
while (enumerator.hasMoreElements()) {
let handler = enumerator.getNext().QueryInterface(Ci.nsIHandlerApp);
// OS handler share the same name, it's most likely the same app, skipping...
if (handler.name == handlerInfo.defaultDescription) {
continue;
}
@@ -1901,18 +1928,17 @@ var gMainPane = {
try {
this._storeAction(aActionItem);
} finally {
this._storingAction = false;
}
},
_storeAction(aActionItem) {
- var typeItem = this._list.selectedItem;
- var handlerInfo = this._handledTypes[typeItem.type];
+ var handlerInfo = this.selectedHandlerListItem.handlerInfoWrapper;
let action = parseInt(aActionItem.getAttribute("action"));
// Set the plugin state if we're enabling or disabling a plugin.
if (action == kActionUsePlugin)
handlerInfo.enablePluginType();
else if (handlerInfo.pluginName && !handlerInfo.isDisabledPluginType)
handlerInfo.disablePluginType();
@@ -1937,34 +1963,33 @@ 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.
- HandlerListItem.forNode(typeItem).refreshAction();
+ this.selectedHandlerListItem.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];
+ var handlerInfo = this.selectedHandlerListItem.handlerInfoWrapper;
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
- HandlerListItem.forNode(typeItem).refreshAction();
+ this.selectedHandlerListItem.refreshAction();
};
gSubDialog.open("chrome://browser/content/preferences/applicationManager.xul",
"resizable=no", handlerInfo, onComplete);
},
chooseApp(aEvent) {
@@ -1993,17 +2018,17 @@ var gMainPane = {
break;
}
}
}
};
if (AppConstants.platform == "win") {
var params = {};
- var handlerInfo = this._handledTypes[this._list.selectedItem.type];
+ var handlerInfo = this.selectedHandlerListItem.handlerInfoWrapper;
if (isFeedType(handlerInfo.type)) {
// MIME info will be null, create a temp object.
params.mimeInfo = gMIMEService.getFromTypeAndExtension(handlerInfo.type,
handlerInfo.primaryExtension);
} else {
params.mimeInfo = handlerInfo.wrappedHandlerInfo;
}
@@ -2033,17 +2058,17 @@ var gMainPane = {
if (aResult == Ci.nsIFilePicker.returnOK && fp.file &&
this._isValidHandlerExecutable(fp.file)) {
handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
createInstance(Ci.nsILocalHandlerApp);
handlerApp.name = getFileDisplayName(fp.file);
handlerApp.executable = fp.file;
// Add the app to the type's list of possible handlers.
- let handler = this._handledTypes[this._list.selectedItem.type];
+ let handler = this.selectedHandlerListItem.handlerInfoWrapper;
handler.addPossibleApplicationHandler(handlerApp);
chooseAppCallback(handlerApp);
}
};
// 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.
@@ -2459,16 +2484,23 @@ class HandlerListItem {
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);
}
}
+
+ set showActionsMenu(value) {
+ document.getAnonymousElementByAttribute(this.node, "anonid", "selected")
+ .setAttribute("hidden", !value);
+ document.getAnonymousElementByAttribute(this.node, "anonid", "not-selected")
+ .setAttribute("hidden", !!value);
+ }
}
/**
* 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