зеркало из https://github.com/mozilla/pjs.git
bug 395182: also display the MIME types for types with identical descriptions in the Applications prefpane; r=gavin
This commit is contained in:
Родитель
6333deb41a
Коммит
dce9dc2693
|
@ -668,12 +668,6 @@ var feedHandlerInfo = {
|
|||
},
|
||||
|
||||
|
||||
//**************************************************************************//
|
||||
// Plugin Handling
|
||||
|
||||
handledOnlyByPlugin: false,
|
||||
|
||||
|
||||
//**************************************************************************//
|
||||
// Storage
|
||||
|
||||
|
@ -706,6 +700,21 @@ var gApplicationsPane = {
|
|||
// 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
|
||||
// what types we can show or change the sort column/direction.
|
||||
// Note: this isn't necessarily the list of types we *will* show; if the user
|
||||
// provides a filter string, we'll only show the subset of types in this list
|
||||
// that match that string.
|
||||
_visibleTypes: [],
|
||||
|
||||
// A count of the number of times each visible type description appears.
|
||||
// We use these counts to determine whether or not to annotate descriptions
|
||||
// with their types to distinguish duplicate descriptions from each other.
|
||||
// A hash of integer counts, indexed by string description.
|
||||
_visibleTypeDescriptionCount: {},
|
||||
|
||||
|
||||
//**************************************************************************//
|
||||
|
@ -760,7 +769,7 @@ var gApplicationsPane = {
|
|||
window.addEventListener("unload", this, false);
|
||||
|
||||
// Figure out how we should be sorting the list. We persist sort settings
|
||||
// across sessions, so we can't assume the default sort column and direction.
|
||||
// across sessions, so we can't assume the default sort column/direction.
|
||||
// XXX should we be using the XUL sort service instead?
|
||||
if (document.getElementById("typeColumn").hasAttribute("sortDirection"))
|
||||
this._sortColumn = document.getElementById("typeColumn");
|
||||
|
@ -776,7 +785,9 @@ var gApplicationsPane = {
|
|||
// XXX Shouldn't we perhaps just set a max-height on the richlistbox?
|
||||
var _delayedPaneLoad = function(self) {
|
||||
self._loadData();
|
||||
self.rebuildView();
|
||||
self._rebuildVisibleTypes();
|
||||
self._sortVisibleTypes();
|
||||
self._rebuildView();
|
||||
}
|
||||
setTimeout(_delayedPaneLoad, 0, this);
|
||||
},
|
||||
|
@ -811,8 +822,19 @@ var gApplicationsPane = {
|
|||
observe: function (aSubject, aTopic, aData) {
|
||||
// Rebuild the list when there are changes to preferences that influence
|
||||
// whether or not to show certain entries in the list.
|
||||
if (aTopic == "nsPref:changed")
|
||||
this.rebuildView();
|
||||
if (aTopic == "nsPref:changed") {
|
||||
// These two prefs alter the list of visible types, so we have to rebuild
|
||||
// that list when they change.
|
||||
if (aData == PREF_SHOW_PLUGINS_IN_LIST ||
|
||||
aData == PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS) {
|
||||
this._rebuildVisibleTypes();
|
||||
this._sortVisibleTypes();
|
||||
}
|
||||
|
||||
// All the prefs we observe can affect what we display, so we rebuild
|
||||
// the view when any of them changes.
|
||||
this._rebuildView();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
@ -837,6 +859,7 @@ var gApplicationsPane = {
|
|||
|
||||
_loadFeedHandler: function() {
|
||||
this._handledTypes[TYPE_MAYBE_FEED] = feedHandlerInfo;
|
||||
feedHandlerInfo.handledOnlyByPlugin = false;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -864,19 +887,19 @@ var gApplicationsPane = {
|
|||
let plugin = navigator.plugins[i];
|
||||
for (let j = 0; j < plugin.length; ++j) {
|
||||
let type = plugin[j].type;
|
||||
let handlerInfoWrapper;
|
||||
|
||||
if (typeof this._handledTypes[type] == "undefined") {
|
||||
let handlerInfoWrapper;
|
||||
if (type in this._handledTypes)
|
||||
handlerInfoWrapper = this._handledTypes[type];
|
||||
else {
|
||||
let wrappedHandlerInfo =
|
||||
this._mimeSvc.getFromTypeAndExtension(type, null);
|
||||
handlerInfoWrapper = new HandlerInfoWrapper(type, wrappedHandlerInfo);
|
||||
handlerInfoWrapper.handledOnlyByPlugin = true;
|
||||
this._handledTypes[type] = handlerInfoWrapper;
|
||||
}
|
||||
else
|
||||
handlerInfoWrapper = this._handledTypes[type];
|
||||
|
||||
handlerInfoWrapper.plugin = plugin;
|
||||
handlerInfoWrapper.handledOnlyByPlugin = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -887,17 +910,17 @@ var gApplicationsPane = {
|
|||
_loadApplicationHandlers: function() {
|
||||
var wrappedHandlerInfos = this._handlerSvc.enumerate();
|
||||
while (wrappedHandlerInfos.hasMoreElements()) {
|
||||
let wrappedHandlerInfo = wrappedHandlerInfos.getNext().
|
||||
QueryInterface(Ci.nsIHandlerInfo);
|
||||
let wrappedHandlerInfo =
|
||||
wrappedHandlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
|
||||
let type = wrappedHandlerInfo.type;
|
||||
let handlerInfoWrapper;
|
||||
|
||||
if (typeof this._handledTypes[type] == "undefined") {
|
||||
let handlerInfoWrapper;
|
||||
if (type in this._handledTypes)
|
||||
handlerInfoWrapper = this._handledTypes[type];
|
||||
else {
|
||||
handlerInfoWrapper = new HandlerInfoWrapper(type, wrappedHandlerInfo);
|
||||
this._handledTypes[type] = handlerInfoWrapper;
|
||||
}
|
||||
else
|
||||
handlerInfoWrapper = this._handledTypes[type];
|
||||
|
||||
handlerInfoWrapper.handledOnlyByPlugin = false;
|
||||
}
|
||||
|
@ -907,35 +930,12 @@ var gApplicationsPane = {
|
|||
//**************************************************************************//
|
||||
// View Construction
|
||||
|
||||
rebuildView: function() {
|
||||
// Clear the list of entries.
|
||||
while (this._list.childNodes.length > 1)
|
||||
this._list.removeChild(this._list.lastChild);
|
||||
|
||||
var visibleTypes = this._getVisibleTypes();
|
||||
|
||||
if (this._sortColumn)
|
||||
this._sortTypes(visibleTypes);
|
||||
|
||||
for each (let visibleType in visibleTypes) {
|
||||
let item = document.createElement("richlistitem");
|
||||
item.setAttribute("type", visibleType.type);
|
||||
item.setAttribute("typeDescription", visibleType.description);
|
||||
if (visibleType.smallIcon)
|
||||
item.setAttribute("typeIcon", visibleType.smallIcon);
|
||||
item.setAttribute("actionDescription",
|
||||
this._describePreferredAction(visibleType));
|
||||
item.setAttribute("actionIcon",
|
||||
this._getIconURLForPreferredAction(visibleType));
|
||||
this._list.appendChild(item);
|
||||
}
|
||||
|
||||
this._selectLastSelectedType();
|
||||
},
|
||||
|
||||
_getVisibleTypes: function() {
|
||||
var visibleTypes = [];
|
||||
_rebuildVisibleTypes: function() {
|
||||
// Reset the list of visible types and the visible type description counts.
|
||||
this._visibleTypes = [];
|
||||
this._visibleTypeDescriptionCount = {};
|
||||
|
||||
// Get the preferences that help determine what types to show.
|
||||
var showPlugins = this._prefSvc.getBoolPref(PREF_SHOW_PLUGINS_IN_LIST);
|
||||
var hideTypesWithoutExtensions =
|
||||
this._prefSvc.getBoolPref(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS);
|
||||
|
@ -958,23 +958,68 @@ var gApplicationsPane = {
|
|||
if (handlerInfo.handledOnlyByPlugin && !showPlugins)
|
||||
continue;
|
||||
|
||||
// If the user is filtering the list, then only show matching types.
|
||||
if (this._filter.value && !this._matchesFilter(handlerInfo))
|
||||
continue;
|
||||
|
||||
// We couldn't find any reason to exclude the type, so include it.
|
||||
visibleTypes.push(handlerInfo);
|
||||
this._visibleTypes.push(handlerInfo);
|
||||
|
||||
if (handlerInfo.description in this._visibleTypeDescriptionCount)
|
||||
this._visibleTypeDescriptionCount[handlerInfo.description]++;
|
||||
else
|
||||
this._visibleTypeDescriptionCount[handlerInfo.description] = 1;
|
||||
}
|
||||
},
|
||||
|
||||
_rebuildView: function() {
|
||||
// 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 each (let visibleType in visibleTypes) {
|
||||
let item = document.createElement("richlistitem");
|
||||
item.setAttribute("type", visibleType.type);
|
||||
item.setAttribute("typeDescription", this._describeType(visibleType));
|
||||
if (visibleType.smallIcon)
|
||||
item.setAttribute("typeIcon", visibleType.smallIcon);
|
||||
item.setAttribute("actionDescription",
|
||||
this._describePreferredAction(visibleType));
|
||||
item.setAttribute("actionIcon",
|
||||
this._getIconURLForPreferredAction(visibleType));
|
||||
this._list.appendChild(item);
|
||||
}
|
||||
|
||||
return visibleTypes;
|
||||
this._selectLastSelectedType();
|
||||
},
|
||||
|
||||
_matchesFilter: function(aType) {
|
||||
var filterValue = this._filter.value.toLowerCase();
|
||||
return aType.description.toLowerCase().indexOf(filterValue) != -1 ||
|
||||
return this._describeType(aType).toLowerCase().indexOf(filterValue) != -1 ||
|
||||
this._describePreferredAction(aType).toLowerCase().indexOf(filterValue) != -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Describe, in a human-readable fashion, the type represented by the given
|
||||
* handler info object. Normally this is just the description provided by
|
||||
* the info object, but if more than one object presents the same description,
|
||||
* then we annotate the duplicate descriptions with the type itself to help
|
||||
* users distinguish between those types.
|
||||
*
|
||||
* @param aHandlerInfo {nsIHandlerInfo} the type being described
|
||||
* @returns {string} a description of the type
|
||||
*/
|
||||
_describeType: function(aHandlerInfo) {
|
||||
if (this._visibleTypeDescriptionCount[aHandlerInfo.description] > 1)
|
||||
return this._prefsBundle.getFormattedString("typeDescriptionWithType",
|
||||
[aHandlerInfo.description,
|
||||
aHandlerInfo.type]);
|
||||
|
||||
return aHandlerInfo.description;
|
||||
},
|
||||
|
||||
/**
|
||||
* Describe, in a human-readable fashion, the preferred action to take on
|
||||
* the type represented by the given handler info object.
|
||||
|
@ -985,6 +1030,7 @@ var gApplicationsPane = {
|
|||
*
|
||||
* @param aHandlerInfo {nsIHandlerInfo} the type whose preferred action
|
||||
* is being described
|
||||
* @returns {string} a description of the action
|
||||
*/
|
||||
_describePreferredAction: function(aHandlerInfo) {
|
||||
// alwaysAskBeforeHandling overrides the preferred action, so if that flag
|
||||
|
@ -1248,23 +1294,24 @@ var gApplicationsPane = {
|
|||
else
|
||||
column.setAttribute("sortDirection", "ascending");
|
||||
|
||||
this.rebuildView();
|
||||
this._sortVisibleTypes();
|
||||
this._rebuildView();
|
||||
},
|
||||
|
||||
/**
|
||||
* Given an array of HandlerInfoWrapper objects, sort them according to
|
||||
* the current sort order. Used by rebuildView to sort the set of visible
|
||||
* types before building the list from them.
|
||||
* Sort the list of visible types by the current sort column/direction.
|
||||
*/
|
||||
_sortTypes: function(aTypes) {
|
||||
_sortVisibleTypes: function() {
|
||||
if (!this._sortColumn)
|
||||
return;
|
||||
|
||||
var t = this;
|
||||
|
||||
function sortByType(a, b) {
|
||||
return a.description.toLowerCase().localeCompare(b.description.toLowerCase());
|
||||
return t._describeType(a).toLowerCase().
|
||||
localeCompare(t._describeType(b).toLowerCase());
|
||||
}
|
||||
|
||||
var t = this;
|
||||
function sortByAction(a, b) {
|
||||
return t._describePreferredAction(a).toLowerCase().
|
||||
localeCompare(t._describePreferredAction(b).toLowerCase());
|
||||
|
@ -1272,15 +1319,15 @@ var gApplicationsPane = {
|
|||
|
||||
switch (this._sortColumn.getAttribute("value")) {
|
||||
case "type":
|
||||
aTypes.sort(sortByType);
|
||||
this._visibleTypes.sort(sortByType);
|
||||
break;
|
||||
case "action":
|
||||
aTypes.sort(sortByAction);
|
||||
this._visibleTypes.sort(sortByAction);
|
||||
break;
|
||||
}
|
||||
|
||||
if (this._sortColumn.getAttribute("sortDirection") == "descending")
|
||||
aTypes.reverse();
|
||||
this._visibleTypes.reverse();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1292,7 +1339,7 @@ var gApplicationsPane = {
|
|||
return;
|
||||
}
|
||||
|
||||
this.rebuildView();
|
||||
this._rebuildView();
|
||||
|
||||
document.getElementById("clearFilter").disabled = false;
|
||||
},
|
||||
|
@ -1313,7 +1360,7 @@ var gApplicationsPane = {
|
|||
|
||||
clearFilter: function() {
|
||||
this._filter.value = "";
|
||||
this.rebuildView();
|
||||
this._rebuildView();
|
||||
|
||||
this._filter.focus();
|
||||
document.getElementById("clearFilter").disabled = true;
|
||||
|
|
|
@ -61,6 +61,11 @@ pluginName=%S (in %S)
|
|||
previewInApp=Preview in %S
|
||||
liveBookmarksInApp=Live Bookmarks in %S
|
||||
|
||||
# LOCALIZATION NOTE (typeDescriptionWithType):
|
||||
# %1$S = type description (for example "Portable Document Format")
|
||||
# %2$S = type (for example "application/pdf")
|
||||
typeDescriptionWithType=%S (%S)
|
||||
|
||||
|
||||
#### Cookie Viewer
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче