Bug 865916: create a Character Encoding widget and subview. r=gkruitbosch,dao

This commit is contained in:
Mike de Boer 2013-08-20 14:52:29 +02:00
parent 4b36c032d8
commit b4e57aae97
11 changed files with 346 additions and 32 deletions

View File

@ -106,6 +106,20 @@
<vbox id="PanelUI-developerItems"/>
</panelview>
<panelview id="PanelUI-characterEncodingView" flex="1">
<label value="&charsetMenu.label;"/>
<toolbarbutton label="&charsetCustomize.label;"
oncommand="PanelUI.onCharsetCustomizeCommand();"/>
<vbox id="PanelUI-characterEncodingView-customlist"
class="PanelUI-characterEncodingView-list"/>
<vbox>
<label value="&charsetMenuAutodet.label;"/>
<vbox id="PanelUI-characterEncodingView-autodetect"
class="PanelUI-characterEncodingView-list"/>
</vbox>
</panelview>
</panelmultiview>
<popupset id="customizationContextMenus">
<menupopup id="customizationContextMenu">

View File

@ -280,6 +280,17 @@ const PanelUI = {
}
},
/**
* Open a dialog window that allow the user to customize listed character sets.
*/
onCharsetCustomizeCommand: function() {
this.hide();
window.openDialog("chrome://global/content/customizeCharset.xul",
"PrefWindow",
"chrome,modal=yes,resizable=yes",
"browser");
},
/**
* Signal that we're about to make a lot of changes to the contents of the
* panels all at once. For performance, we ignore the mutations.

View File

@ -129,22 +129,31 @@ let CustomizableUIInternal = {
this._defineBuiltInWidgets();
this.loadSavedState();
let panelPlacements = [
"edit-controls",
"zoom-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button",
];
let showCharacterEncoding = Services.prefs.getComplexValue(
"browser.menu.showCharacterEncoding",
Ci.nsIPrefLocalizedString
).data;
if (showCharacterEncoding == "true") {
panelPlacements.push("characterencoding-button");
}
this.registerArea(CustomizableUI.AREA_PANEL, {
anchor: "PanelUI-menu-button",
type: CustomizableUI.TYPE_MENU_PANEL,
defaultPlacements: [
"edit-controls",
"zoom-controls",
"new-window-button",
"privatebrowsing-button",
"save-page-button",
"print-button",
"history-panelmenu",
"fullscreen-button",
"find-button",
"preferences-button",
"add-ons-button",
]
defaultPlacements: panelPlacements
});
this.registerArea(CustomizableUI.AREA_NAVBAR, {
legacy: true,
@ -1515,6 +1524,7 @@ let CustomizableUIInternal = {
//XXXunf Log some warnings here, when the data provided isn't up to scratch.
normalizeWidget: function(aData, aSource) {
let widget = {
implementation: aData,
source: aSource || "addon",
instances: new Map(),
currentArea: null,
@ -1532,6 +1542,9 @@ let CustomizableUIInternal = {
return null;
}
delete widget.implementation.currentArea;
widget.implementation.__defineGetter__("currentArea", function() widget.currentArea);
const kReqStringProps = ["id"];
for (let prop of kReqStringProps) {
if (typeof aData[prop] != "string") {
@ -1573,9 +1586,8 @@ let CustomizableUIInternal = {
widget.disabled = aData.disabled === true;
widget.onClick = typeof aData.onClick == "function" ? aData.onClick : null;
widget.onCreated = typeof aData.onCreated == "function" ? aData.onCreated : null;
this.wrapWidgetEventHandler("onClick", widget);
this.wrapWidgetEventHandler("onCreated", widget);
if (widget.type == "button") {
widget.onCommand = typeof aData.onCommand == "function" ?
@ -1589,16 +1601,10 @@ let CustomizableUIInternal = {
}
widget.viewId = aData.viewId;
widget.onViewShowing = typeof aData.onViewShowing == "function" ?
aData.onViewShowing :
null;
widget.onViewHiding = typeof aData.onViewHiding == "function" ?
aData.onViewHiding :
null;
this.wrapWidgetEventHandler("onViewShowing", widget);
this.wrapWidgetEventHandler("onViewHiding", widget);
} else if (widget.type == "custom") {
widget.onBuild = typeof aData.onBuild == "function" ?
aData.onBuild :
null;
this.wrapWidgetEventHandler("onBuild", widget);
}
if (gPalette.has(widget.id)) {
@ -1608,6 +1614,27 @@ let CustomizableUIInternal = {
return widget;
},
wrapWidgetEventHandler: function(aEventName, aWidget) {
if (typeof aWidget.implementation[aEventName] != "function") {
aWidget[aEventName] = null;
return;
}
aWidget[aEventName] = function(...aArgs) {
// Wrap inside a try...catch to properly log errors, until bug 862627 is
// fixed, which in turn might help bug 503244.
try {
// Don't copy the function to the normalized widget object, instead
// keep it on the original object provided to the API so that
// additional methods can be implemented and used by the event
// handlers.
return aWidget.implementation[aEventName].apply(aWidget.implementation,
aArgs);
} catch (e) {
Cu.reportError(e);
}
};
},
destroyWidget: function(aWidgetId) {
let widget = gPalette.get(aWidgetId);
if (!widget) {
@ -1948,7 +1975,7 @@ this.CustomizableUI = {
},
removePanelCloseListeners: function(aPanel) {
CustomizableUIInternal.removePanelCloseListeners(aPanel);
},
}
};
Object.freeze(this.CustomizableUI);

View File

@ -12,6 +12,9 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "CharsetManager",
"@mozilla.org/charset-converter-manager;1",
"nsICharsetConverterManager");
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
@ -589,4 +592,214 @@ const CustomizableWidgets = [{
node.setAttribute("disabled", "true");
}
}
}, {
id: "characterencoding-button",
type: "view",
viewId: "PanelUI-characterEncodingView",
removable: true,
defaultArea: CustomizableUI.AREA_PANEL,
allowedAreas: [CustomizableUI.AREA_PANEL],
maybeDisableMenu: function(aDocument) {
let window = aDocument.defaultView;
return !(window.gBrowser &&
window.gBrowser.docShell &&
window.gBrowser.docShell.mayEnableCharacterEncodingMenu);
},
getCharsetList: function(aSection, aDocument) {
let currCharset = aDocument.defaultView.content.document.characterSet;
let list = "";
try {
let pref = "intl.charsetmenu.browser." + aSection;
list = Services.prefs.getComplexValue(pref,
Ci.nsIPrefLocalizedString).data;
} catch (e) {}
list = list.trim();
if (!list)
return [];
list = list.split(",");
let items = [];
for (let charset of list) {
charset = charset.trim();
let notForBrowser = false;
try {
notForBrowser = CharsetManager.getCharsetData(charset,
"notForBrowser");
} catch (e) {}
if (notForBrowser)
continue;
let title = charset;
try {
title = CharsetManager.getCharsetTitle(charset);
} catch (e) {}
items.push({value: charset, name: title, current: charset == currCharset});
}
return items;
},
getAutoDetectors: function(aDocument) {
let detectorEnum = CharsetManager.GetCharsetDetectorList();
let currDetector;
try {
currDetector = Services.prefs.getComplexValue(
"intl.charset.detector", Ci.nsIPrefLocalizedString).data;
} catch (e) {}
if (!currDetector)
currDetector = "off";
currDetector = "chardet." + currDetector;
let items = [];
while (detectorEnum.hasMore()) {
let detector = detectorEnum.getNext();
let title = detector;
try {
title = CharsetManager.getCharsetTitle(detector);
} catch (e) {}
items.push({value: detector, name: title, current: detector == currDetector});
}
items.sort((aItem1, aItem2) => {
return aItem1.name.localeCompare(aItem2.name);
});
return items;
},
populateList: function(aDocument, aContainerId, aSection) {
let containerElem = aDocument.getElementById(aContainerId);
while (containerElem.firstChild) {
containerElem.removeChild(containerElem.firstChild);
}
containerElem.addEventListener("command", this.onCommand, false);
let list = [];
if (aSection == "autodetect") {
list = this.getAutoDetectors(aDocument);
} else if (aSection == "browser") {
let staticList = this.getCharsetList("static", aDocument);
let cacheList = this.getCharsetList("cache", aDocument);
// Combine lists, and de-duplicate.
let checkedIn = new Set();
for (let item of staticList.concat(cacheList)) {
let itemName = item.name.toLowerCase();
if (!checkedIn.has(itemName)) {
list.push(item);
checkedIn.add(itemName);
}
}
}
// Update the appearance of the buttons when it's not possible to
// customize encoding.
let disabled = this.maybeDisableMenu(aDocument);
for (let item of list) {
let elem = aDocument.createElementNS(kNSXUL, "toolbarbutton");
elem.setAttribute("label", item.name);
elem.section = aSection;
elem.value = item.value;
if (item.current)
elem.setAttribute("current", "true");
if (disabled)
elem.setAttribute("disabled", "true");
containerElem.appendChild(elem);
}
},
onViewShowing: function(aEvent) {
let document = aEvent.target.ownerDocument;
this.populateList(document,
"PanelUI-characterEncodingView-customlist",
"browser");
this.populateList(document,
"PanelUI-characterEncodingView-autodetect",
"autodetect");
},
onCommand: function(aEvent) {
let node = aEvent.target;
if (!node.hasAttribute || !node.section) {
return;
}
CustomizableUI.hidePanelForNode(node);
let window = node.ownerDocument.defaultView;
let section = node.section;
let value = node.value;
// The behavior as implemented here is directly based off of the
// `MultiplexHandler()` method in browser.js.
if (section == "browser") {
window.BrowserSetForcedCharacterSet(value);
} else if (section == "autodetect") {
value = value.replace(/^chardet\./, "");
if (value == "off") {
value = "";
}
// Set the detector pref.
try {
let str = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
str.data = value;
Services.prefs.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
} catch (e) {
Cu.reportError("Failed to set the intl.charset.detector preference.");
}
// Prepare a browser page reload with a changed charset.
window.BrowserCharsetReload();
}
},
onCreated: function(aNode) {
const kPanelId = "PanelUI-popup";
let document = aNode.ownerDocument;
let updateButton = () => {
if (this.maybeDisableMenu(document))
aNode.setAttribute("disabled", "true");
else
aNode.removeAttribute("disabled");
};
if (this.currentArea == CustomizableUI.AREA_PANEL) {
let panel = document.getElementById(kPanelId);
panel.addEventListener("popupshowing", updateButton);
}
let listener = {
onWidgetAdded: (aWidgetId, aArea) => {
if (aWidgetId != this.id)
return;
if (aArea == CustomizableUI.AREA_PANEL) {
let panel = document.getElementById(kPanelId);
panel.addEventListener("popupshowing", updateButton);
}
},
onWidgetRemoved: (aWidgetId, aPrevArea) => {
if (aWidgetId != this.id)
return;
if (aPrevArea == CustomizableUI.AREA_PANEL) {
let panel = document.getElementById(kPanelId);
panel.removeEventListener("popupshowing", updateButton);
}
},
onWidgetInstanceRemoved: (aWidgetId, aDoc) => {
if (aWidgetId != this.id || aDoc != document)
return;
CustomizableUI.removeListener(listener);
let panel = aDoc.getElementById(kPanelId);
panel.removeEventListener("popupshowing", updateButton);
}
};
CustomizableUI.addListener(listener);
}
}];

View File

@ -63,5 +63,10 @@ copy-button.tooltiptext = Copy
paste-button.label = Paste
paste-button.tooltiptext = Paste
# LOCALIZATION NOTE (feed-button.tooltiptext): Use the unicode ellipsis char,
# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
feed-button.label = Subscribe
feed-button.tooltiptext = Subscribe to this page…
characterencoding-button.label = Character Encoding
characterencoding-button.tooltiptext = Character encoding

View File

@ -715,18 +715,18 @@ toolbar > .customization-target > toolbarpaletteitem > #history-panelmenu {
list-style-image: url("moz-icon://stock/gtk-home?size=menu&state=disabled");
}
#characterencoding-panelmenu[customizableui-areatype="toolbar"],
#characterencoding-button[customizableui-areatype="toolbar"],
#developer-button[customizableui-areatype="toolbar"] {
list-style-image: url(chrome://browser/skin/menuPanel.png);
}
#characterencoding-panelmenu[customizableui-areatype="toolbar"] > .toolbarbutton-icon,
#characterencoding-button[customizableui-areatype="toolbar"] > .toolbarbutton-icon,
#developer-button[customizableui-areatype="toolbar"] > .toolbarbutton-icon {
height: 24px;
}
#characterencoding-panelmenu[customizableui-areatype="toolbar"] {
-moz-image-region: rect(0px, 216px, 24px, 192px);
#characterencoding-button[customizableui-areatype="toolbar"] {
-moz-image-region: rect(0px, 480px, 32px, 448px);
}
#developer-button[customizableui-areatype="toolbar"] {

View File

@ -475,10 +475,14 @@ toolbarbutton.bookmark-item > menupopup {
-moz-image-region: rect(18px, 306px, 36px, 288px);
}
#charset-button@toolbarButtonPressed@ {
#characterencoding-button@toolbarButtonPressed@ {
-moz-image-region: rect(18px, 324px, 36px, 306px);
}
#characterencoding-button[open] {
-moz-image-region: rect(36px, 324px, 54px, 306px);
}
#new-window-button@toolbarButtonPressed@ {
-moz-image-region: rect(18px, 342px, 36px, 324px);
}
@ -696,6 +700,18 @@ toolbarbutton.bookmark-item > menupopup {
-moz-image-region: rect(36px, 576px, 72px, 540px);
}
#characterencoding-button[customizableui-areatype="toolbar"] {
-moz-image-region: rect(0, 648px, 36px, 612px);
}
#characterencoding-button[customizableui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
-moz-image-region: rect(36px, 648px, 72px, 612px);
}
#characterencoding-button[customizableui-areatype="toolbar"][open] {
-moz-image-region: rect(72px, 648px, 108px, 612px);
}
#new-window-button[customizableui-areatype="toolbar"] {
-moz-image-region: rect(0, 684px, 36px, 648px);
}
@ -918,6 +934,11 @@ toolbarbutton.bookmark-item > menupopup {
-moz-image-region: rect(0px, 896px, 64px, 832px);
}
#characterencoding-button[customizableui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #characterencoding-button {
-moz-image-region: rect(0, 960px, 64px, 896px);
}
#new-window-button[customizableui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #new-window-button {
-moz-image-region: rect(0px, 1024px, 64px, 960px);

View File

@ -1,3 +1,3 @@
%filter substitution
%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #downloads-indicator, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-reset-button, #zoom-in-button, #sync-button, #feed-button, #alltabs-button, #tabview-button, #webrtc-status-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button
%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #downloads-indicator, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-reset-button, #zoom-in-button, #sync-button, #feed-button, #alltabs-button, #tabview-button, #webrtc-status-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button, #characterencoding-button

View File

@ -352,3 +352,17 @@ toolbarbutton.panel-multiview-anchor {
border-bottom-left-radius: 2px;
}
.PanelUI-characterEncodingView-list > toolbarbutton[current] {
-moz-padding-start: 2px;
}
.PanelUI-characterEncodingView-list > toolbarbutton[current] > .toolbarbutton-text,
#customizationui-widget-panel .PanelUI-characterEncodingView-list > toolbarbutton[current] > .toolbarbutton-text {
-moz-padding-start: 0px;
}
.PanelUI-characterEncodingView-list > toolbarbutton[current]::before {
content: "✓";
display: -moz-box;
width: 12px;
}

View File

@ -60,6 +60,11 @@ toolbarpaletteitem[place="palette"] > #social-share-button {
-moz-image-region: rect(0px, 448px, 32px, 416px);
}
#characterencoding-button[customizableui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #characterencoding-button {
-moz-image-region: rect(0px, 480px, 32px, 448px);
}
#new-window-button[customizableui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #new-window-button {
-moz-image-region: rect(0px, 512px, 32px, 480px);

View File

@ -66,6 +66,10 @@
-moz-image-region: rect(0, 288px, 18px, 270px);
}
#characterencoding-button[customizableui-areatype="toolbar"]{
-moz-image-region: rect(0, 324px, 18px, 306px);
}
#new-window-button[customizableui-areatype="toolbar"] {
-moz-image-region: rect(0, 342px, 18px, 324px);
}