1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/customizableui/src/CustomizableWidgets.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,969 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; 1.10 + 1.11 +this.EXPORTED_SYMBOLS = ["CustomizableWidgets"]; 1.12 + 1.13 +Cu.import("resource:///modules/CustomizableUI.jsm"); 1.14 +Cu.import("resource://gre/modules/Services.jsm"); 1.15 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.16 +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", 1.17 + "resource://gre/modules/PlacesUtils.jsm"); 1.18 +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils", 1.19 + "resource:///modules/PlacesUIUtils.jsm"); 1.20 +XPCOMUtils.defineLazyModuleGetter(this, "RecentlyClosedTabsAndWindowsMenuUtils", 1.21 + "resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm"); 1.22 +XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils", 1.23 + "resource://gre/modules/ShortcutUtils.jsm"); 1.24 +XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu", 1.25 + "resource://gre/modules/CharsetMenu.jsm"); 1.26 +XPCOMUtils.defineLazyServiceGetter(this, "CharsetManager", 1.27 + "@mozilla.org/charset-converter-manager;1", 1.28 + "nsICharsetConverterManager"); 1.29 + 1.30 +XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() { 1.31 + const kCharsetBundle = "chrome://global/locale/charsetMenu.properties"; 1.32 + return Services.strings.createBundle(kCharsetBundle); 1.33 +}); 1.34 +XPCOMUtils.defineLazyGetter(this, "BrandBundle", function() { 1.35 + const kBrandBundle = "chrome://branding/locale/brand.properties"; 1.36 + return Services.strings.createBundle(kBrandBundle); 1.37 +}); 1.38 + 1.39 +const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.40 +const kPrefCustomizationDebug = "browser.uiCustomization.debug"; 1.41 +const kWidePanelItemClass = "panel-wide-item"; 1.42 + 1.43 +let gModuleName = "[CustomizableWidgets]"; 1.44 +#include logging.js 1.45 + 1.46 +function setAttributes(aNode, aAttrs) { 1.47 + let doc = aNode.ownerDocument; 1.48 + for (let [name, value] of Iterator(aAttrs)) { 1.49 + if (!value) { 1.50 + if (aNode.hasAttribute(name)) 1.51 + aNode.removeAttribute(name); 1.52 + } else { 1.53 + if (name == "shortcutId") { 1.54 + continue; 1.55 + } 1.56 + if (name == "label" || name == "tooltiptext") { 1.57 + let stringId = (typeof value == "string") ? value : name; 1.58 + let additionalArgs = []; 1.59 + if (aAttrs.shortcutId) { 1.60 + let shortcut = doc.getElementById(aAttrs.shortcutId); 1.61 + if (doc) { 1.62 + additionalArgs.push(ShortcutUtils.prettifyShortcut(shortcut)); 1.63 + } 1.64 + } 1.65 + value = CustomizableUI.getLocalizedProperty({id: aAttrs.id}, stringId, additionalArgs); 1.66 + } 1.67 + aNode.setAttribute(name, value); 1.68 + } 1.69 + } 1.70 +} 1.71 + 1.72 +function updateCombinedWidgetStyle(aNode, aArea, aModifyCloseMenu) { 1.73 + let inPanel = (aArea == CustomizableUI.AREA_PANEL); 1.74 + let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1 toolbarbutton-combined"; 1.75 + let attrs = {class: cls}; 1.76 + if (aModifyCloseMenu) { 1.77 + attrs.closemenu = inPanel ? "none" : null; 1.78 + } 1.79 + attrs["cui-areatype"] = aArea ? CustomizableUI.getAreaType(aArea) : null; 1.80 + for (let i = 0, l = aNode.childNodes.length; i < l; ++i) { 1.81 + if (aNode.childNodes[i].localName == "separator") 1.82 + continue; 1.83 + setAttributes(aNode.childNodes[i], attrs); 1.84 + } 1.85 +} 1.86 + 1.87 +function addShortcut(aNode, aDocument, aItem) { 1.88 + let shortcutId = aNode.getAttribute("key"); 1.89 + if (!shortcutId) { 1.90 + return; 1.91 + } 1.92 + let shortcut = aDocument.getElementById(shortcutId); 1.93 + if (!shortcut) { 1.94 + return; 1.95 + } 1.96 + aItem.setAttribute("shortcut", ShortcutUtils.prettifyShortcut(shortcut)); 1.97 +} 1.98 + 1.99 +function fillSubviewFromMenuItems(aMenuItems, aSubview) { 1.100 + let attrs = ["oncommand", "onclick", "label", "key", "disabled", 1.101 + "command", "observes", "hidden", "class", "origin", 1.102 + "image", "checked"]; 1.103 + 1.104 + let doc = aSubview.ownerDocument; 1.105 + let fragment = doc.createDocumentFragment(); 1.106 + for (let menuChild of aMenuItems) { 1.107 + if (menuChild.hidden) 1.108 + continue; 1.109 + 1.110 + let subviewItem; 1.111 + if (menuChild.localName == "menuseparator") { 1.112 + // Don't insert duplicate or leading separators. This can happen if there are 1.113 + // menus (which we don't copy) above the separator. 1.114 + if (!fragment.lastChild || fragment.lastChild.localName == "menuseparator") { 1.115 + continue; 1.116 + } 1.117 + subviewItem = doc.createElementNS(kNSXUL, "menuseparator"); 1.118 + } else if (menuChild.localName == "menuitem") { 1.119 + subviewItem = doc.createElementNS(kNSXUL, "toolbarbutton"); 1.120 + addShortcut(menuChild, doc, subviewItem); 1.121 + 1.122 + let item = menuChild; 1.123 + if (!item.hasAttribute("onclick")) { 1.124 + subviewItem.addEventListener("click", event => { 1.125 + let newEvent = new doc.defaultView.MouseEvent(event.type, event); 1.126 + item.dispatchEvent(newEvent); 1.127 + }); 1.128 + } 1.129 + 1.130 + if (!item.hasAttribute("oncommand")) { 1.131 + subviewItem.addEventListener("command", event => { 1.132 + let newEvent = doc.createEvent("XULCommandEvent"); 1.133 + newEvent.initCommandEvent( 1.134 + event.type, event.bubbles, event.cancelable, event.view, 1.135 + event.detail, event.ctrlKey, event.altKey, event.shiftKey, 1.136 + event.metaKey, event.sourceEvent); 1.137 + item.dispatchEvent(newEvent); 1.138 + }); 1.139 + } 1.140 + } else { 1.141 + continue; 1.142 + } 1.143 + for (let attr of attrs) { 1.144 + let attrVal = menuChild.getAttribute(attr); 1.145 + if (attrVal) 1.146 + subviewItem.setAttribute(attr, attrVal); 1.147 + } 1.148 + // We do this after so the .subviewbutton class doesn't get overriden. 1.149 + if (menuChild.localName == "menuitem") { 1.150 + subviewItem.classList.add("subviewbutton"); 1.151 + } 1.152 + fragment.appendChild(subviewItem); 1.153 + } 1.154 + aSubview.appendChild(fragment); 1.155 +} 1.156 + 1.157 +function clearSubview(aSubview) { 1.158 + let parent = aSubview.parentNode; 1.159 + // We'll take the container out of the document before cleaning it out 1.160 + // to avoid reflowing each time we remove something. 1.161 + parent.removeChild(aSubview); 1.162 + 1.163 + while (aSubview.firstChild) { 1.164 + aSubview.firstChild.remove(); 1.165 + } 1.166 + 1.167 + parent.appendChild(aSubview); 1.168 +} 1.169 + 1.170 +const CustomizableWidgets = [{ 1.171 + id: "history-panelmenu", 1.172 + type: "view", 1.173 + viewId: "PanelUI-history", 1.174 + shortcutId: "key_gotoHistory", 1.175 + tooltiptext: "history-panelmenu.tooltiptext2", 1.176 + defaultArea: CustomizableUI.AREA_PANEL, 1.177 + onViewShowing: function(aEvent) { 1.178 + // Populate our list of history 1.179 + const kMaxResults = 15; 1.180 + let doc = aEvent.detail.ownerDocument; 1.181 + 1.182 + let options = PlacesUtils.history.getNewQueryOptions(); 1.183 + options.excludeQueries = true; 1.184 + options.includeHidden = false; 1.185 + options.resultType = options.RESULTS_AS_URI; 1.186 + options.queryType = options.QUERY_TYPE_HISTORY; 1.187 + options.sortingMode = options.SORT_BY_DATE_DESCENDING; 1.188 + options.maxResults = kMaxResults; 1.189 + let query = PlacesUtils.history.getNewQuery(); 1.190 + 1.191 + let items = doc.getElementById("PanelUI-historyItems"); 1.192 + // Clear previous history items. 1.193 + while (items.firstChild) { 1.194 + items.removeChild(items.firstChild); 1.195 + } 1.196 + 1.197 + PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase) 1.198 + .asyncExecuteLegacyQueries([query], 1, options, { 1.199 + handleResult: function (aResultSet) { 1.200 + let onHistoryVisit = function (aUri, aEvent, aItem) { 1.201 + doc.defaultView.openUILink(aUri, aEvent); 1.202 + CustomizableUI.hidePanelForNode(aItem); 1.203 + }; 1.204 + let fragment = doc.createDocumentFragment(); 1.205 + for (let row, i = 0; (row = aResultSet.getNextRow()); i++) { 1.206 + try { 1.207 + let uri = row.getResultByIndex(1); 1.208 + let title = row.getResultByIndex(2); 1.209 + let icon = row.getResultByIndex(6); 1.210 + 1.211 + let item = doc.createElementNS(kNSXUL, "toolbarbutton"); 1.212 + item.setAttribute("label", title || uri); 1.213 + item.setAttribute("targetURI", uri); 1.214 + item.setAttribute("class", "subviewbutton"); 1.215 + item.addEventListener("click", function (aEvent) { 1.216 + onHistoryVisit(uri, aEvent, item); 1.217 + }); 1.218 + if (icon) 1.219 + item.setAttribute("image", "moz-anno:favicon:" + icon); 1.220 + fragment.appendChild(item); 1.221 + } catch (e) { 1.222 + ERROR("Error while showing history subview: " + e); 1.223 + } 1.224 + } 1.225 + items.appendChild(fragment); 1.226 + }, 1.227 + handleError: function (aError) { 1.228 + LOG("History view tried to show but had an error: " + aError); 1.229 + }, 1.230 + handleCompletion: function (aReason) { 1.231 + LOG("History view is being shown!"); 1.232 + }, 1.233 + }); 1.234 + 1.235 + let recentlyClosedTabs = doc.getElementById("PanelUI-recentlyClosedTabs"); 1.236 + while (recentlyClosedTabs.firstChild) { 1.237 + recentlyClosedTabs.removeChild(recentlyClosedTabs.firstChild); 1.238 + } 1.239 + 1.240 + let recentlyClosedWindows = doc.getElementById("PanelUI-recentlyClosedWindows"); 1.241 + while (recentlyClosedWindows.firstChild) { 1.242 + recentlyClosedWindows.removeChild(recentlyClosedWindows.firstChild); 1.243 + } 1.244 + 1.245 +#ifdef MOZ_SERVICES_SYNC 1.246 + let tabsFromOtherComputers = doc.getElementById("sync-tabs-menuitem2"); 1.247 + if (PlacesUIUtils.shouldShowTabsFromOtherComputersMenuitem()) { 1.248 + tabsFromOtherComputers.removeAttribute("hidden"); 1.249 + } else { 1.250 + tabsFromOtherComputers.setAttribute("hidden", true); 1.251 + } 1.252 + 1.253 + if (PlacesUIUtils.shouldEnableTabsFromOtherComputersMenuitem()) { 1.254 + tabsFromOtherComputers.removeAttribute("disabled"); 1.255 + } else { 1.256 + tabsFromOtherComputers.setAttribute("disabled", true); 1.257 + } 1.258 +#endif 1.259 + 1.260 + let utils = RecentlyClosedTabsAndWindowsMenuUtils; 1.261 + let tabsFragment = utils.getTabsFragment(doc.defaultView, "toolbarbutton", true, 1.262 + "menuRestoreAllTabsSubview.label"); 1.263 + let separator = doc.getElementById("PanelUI-recentlyClosedTabs-separator"); 1.264 + let elementCount = tabsFragment.childElementCount; 1.265 + separator.hidden = !elementCount; 1.266 + while (--elementCount >= 0) { 1.267 + tabsFragment.children[elementCount].classList.add("subviewbutton"); 1.268 + } 1.269 + recentlyClosedTabs.appendChild(tabsFragment); 1.270 + 1.271 + let windowsFragment = utils.getWindowsFragment(doc.defaultView, "toolbarbutton", true, 1.272 + "menuRestoreAllWindowsSubview.label"); 1.273 + separator = doc.getElementById("PanelUI-recentlyClosedWindows-separator"); 1.274 + elementCount = windowsFragment.childElementCount; 1.275 + separator.hidden = !elementCount; 1.276 + while (--elementCount >= 0) { 1.277 + windowsFragment.children[elementCount].classList.add("subviewbutton"); 1.278 + } 1.279 + recentlyClosedWindows.appendChild(windowsFragment); 1.280 + }, 1.281 + onCreated: function(aNode) { 1.282 + // Middle clicking recently closed items won't close the panel - cope: 1.283 + let onRecentlyClosedClick = function(aEvent) { 1.284 + if (aEvent.button == 1) { 1.285 + CustomizableUI.hidePanelForNode(this); 1.286 + } 1.287 + }; 1.288 + let doc = aNode.ownerDocument; 1.289 + let recentlyClosedTabs = doc.getElementById("PanelUI-recentlyClosedTabs"); 1.290 + let recentlyClosedWindows = doc.getElementById("PanelUI-recentlyClosedWindows"); 1.291 + recentlyClosedTabs.addEventListener("click", onRecentlyClosedClick); 1.292 + recentlyClosedWindows.addEventListener("click", onRecentlyClosedClick); 1.293 + }, 1.294 + onViewHiding: function(aEvent) { 1.295 + LOG("History view is being hidden!"); 1.296 + } 1.297 + }, { 1.298 + id: "privatebrowsing-button", 1.299 + shortcutId: "key_privatebrowsing", 1.300 + defaultArea: CustomizableUI.AREA_PANEL, 1.301 + onCommand: function(e) { 1.302 + if (e.target && e.target.ownerDocument && e.target.ownerDocument.defaultView) { 1.303 + let win = e.target.ownerDocument.defaultView; 1.304 + if (typeof win.OpenBrowserWindow == "function") { 1.305 + win.OpenBrowserWindow({private: true}); 1.306 + } 1.307 + } 1.308 + } 1.309 + }, { 1.310 + id: "save-page-button", 1.311 + shortcutId: "key_savePage", 1.312 + tooltiptext: "save-page-button.tooltiptext3", 1.313 + defaultArea: CustomizableUI.AREA_PANEL, 1.314 + onCommand: function(aEvent) { 1.315 + let win = aEvent.target && 1.316 + aEvent.target.ownerDocument && 1.317 + aEvent.target.ownerDocument.defaultView; 1.318 + if (win && typeof win.saveDocument == "function") { 1.319 + win.saveDocument(win.content.document); 1.320 + } 1.321 + } 1.322 + }, { 1.323 + id: "find-button", 1.324 + shortcutId: "key_find", 1.325 + tooltiptext: "find-button.tooltiptext3", 1.326 + defaultArea: CustomizableUI.AREA_PANEL, 1.327 + onCommand: function(aEvent) { 1.328 + let win = aEvent.target && 1.329 + aEvent.target.ownerDocument && 1.330 + aEvent.target.ownerDocument.defaultView; 1.331 + if (win && win.gFindBar) { 1.332 + win.gFindBar.onFindCommand(); 1.333 + } 1.334 + } 1.335 + }, { 1.336 + id: "open-file-button", 1.337 + shortcutId: "openFileKb", 1.338 + tooltiptext: "open-file-button.tooltiptext3", 1.339 + defaultArea: CustomizableUI.AREA_PANEL, 1.340 + onCommand: function(aEvent) { 1.341 + let win = aEvent.target 1.342 + && aEvent.target.ownerDocument 1.343 + && aEvent.target.ownerDocument.defaultView; 1.344 + if (win && typeof win.BrowserOpenFileWindow == "function") { 1.345 + win.BrowserOpenFileWindow(); 1.346 + } 1.347 + } 1.348 + }, { 1.349 + id: "developer-button", 1.350 + type: "view", 1.351 + viewId: "PanelUI-developer", 1.352 + shortcutId: "key_devToolboxMenuItem", 1.353 + tooltiptext: "developer-button.tooltiptext2", 1.354 + defaultArea: CustomizableUI.AREA_PANEL, 1.355 + onViewShowing: function(aEvent) { 1.356 + // Populate the subview with whatever menuitems are in the developer 1.357 + // menu. We skip menu elements, because the menu panel has no way 1.358 + // of dealing with those right now. 1.359 + let doc = aEvent.target.ownerDocument; 1.360 + let win = doc.defaultView; 1.361 + 1.362 + let menu = doc.getElementById("menuWebDeveloperPopup"); 1.363 + 1.364 + let itemsToDisplay = [...menu.children]; 1.365 + // Hardcode the addition of the "work offline" menuitem at the bottom: 1.366 + itemsToDisplay.push({localName: "menuseparator", getAttribute: () => {}}); 1.367 + itemsToDisplay.push(doc.getElementById("goOfflineMenuitem")); 1.368 + fillSubviewFromMenuItems(itemsToDisplay, doc.getElementById("PanelUI-developerItems")); 1.369 + 1.370 + }, 1.371 + onViewHiding: function(aEvent) { 1.372 + let doc = aEvent.target.ownerDocument; 1.373 + clearSubview(doc.getElementById("PanelUI-developerItems")); 1.374 + } 1.375 + }, { 1.376 + id: "sidebar-button", 1.377 + type: "view", 1.378 + viewId: "PanelUI-sidebar", 1.379 + tooltiptext: "sidebar-button.tooltiptext2", 1.380 + onViewShowing: function(aEvent) { 1.381 + // Largely duplicated from the developer-button above with a couple minor 1.382 + // alterations. 1.383 + // Populate the subview with whatever menuitems are in the 1.384 + // sidebar menu. We skip menu elements, because the menu panel has no way 1.385 + // of dealing with those right now. 1.386 + let doc = aEvent.target.ownerDocument; 1.387 + let win = doc.defaultView; 1.388 + let menu = doc.getElementById("viewSidebarMenu"); 1.389 + 1.390 + // First clear any existing menuitems then populate. Social sidebar 1.391 + // options may not have been added yet, so we do that here. Add it to the 1.392 + // standard menu first, then copy all sidebar options to the panel. 1.393 + win.SocialSidebar.clearProviderMenus(); 1.394 + let providerMenuSeps = menu.getElementsByClassName("social-provider-menu"); 1.395 + if (providerMenuSeps.length > 0) 1.396 + win.SocialSidebar.populateProviderMenu(providerMenuSeps[0]); 1.397 + 1.398 + fillSubviewFromMenuItems([...menu.children], doc.getElementById("PanelUI-sidebarItems")); 1.399 + }, 1.400 + onViewHiding: function(aEvent) { 1.401 + let doc = aEvent.target.ownerDocument; 1.402 + clearSubview(doc.getElementById("PanelUI-sidebarItems")); 1.403 + } 1.404 + }, { 1.405 + id: "add-ons-button", 1.406 + shortcutId: "key_openAddons", 1.407 + tooltiptext: "add-ons-button.tooltiptext3", 1.408 + defaultArea: CustomizableUI.AREA_PANEL, 1.409 + onCommand: function(aEvent) { 1.410 + let win = aEvent.target && 1.411 + aEvent.target.ownerDocument && 1.412 + aEvent.target.ownerDocument.defaultView; 1.413 + if (win && typeof win.BrowserOpenAddonsMgr == "function") { 1.414 + win.BrowserOpenAddonsMgr(); 1.415 + } 1.416 + } 1.417 + }, { 1.418 + id: "preferences-button", 1.419 + defaultArea: CustomizableUI.AREA_PANEL, 1.420 +#ifdef XP_WIN 1.421 + label: "preferences-button.labelWin", 1.422 + tooltiptext: "preferences-button.tooltipWin2", 1.423 +#else 1.424 +#ifdef XP_MACOSX 1.425 + tooltiptext: "preferences-button.tooltiptext.withshortcut", 1.426 + shortcutId: "key_preferencesCmdMac", 1.427 +#else 1.428 + tooltiptext: "preferences-button.tooltiptext2", 1.429 +#endif 1.430 +#endif 1.431 + onCommand: function(aEvent) { 1.432 + let win = aEvent.target && 1.433 + aEvent.target.ownerDocument && 1.434 + aEvent.target.ownerDocument.defaultView; 1.435 + if (win && typeof win.openPreferences == "function") { 1.436 + win.openPreferences(); 1.437 + } 1.438 + } 1.439 + }, { 1.440 + id: "zoom-controls", 1.441 + type: "custom", 1.442 + tooltiptext: "zoom-controls.tooltiptext2", 1.443 + defaultArea: CustomizableUI.AREA_PANEL, 1.444 + onBuild: function(aDocument) { 1.445 + const kPanelId = "PanelUI-popup"; 1.446 + let areaType = CustomizableUI.getAreaType(this.currentArea); 1.447 + let inPanel = areaType == CustomizableUI.TYPE_MENU_PANEL; 1.448 + let inToolbar = areaType == CustomizableUI.TYPE_TOOLBAR; 1.449 + 1.450 + let buttons = [{ 1.451 + id: "zoom-out-button", 1.452 + command: "cmd_fullZoomReduce", 1.453 + label: true, 1.454 + tooltiptext: "tooltiptext2", 1.455 + shortcutId: "key_fullZoomReduce", 1.456 + }, { 1.457 + id: "zoom-reset-button", 1.458 + command: "cmd_fullZoomReset", 1.459 + tooltiptext: "tooltiptext2", 1.460 + shortcutId: "key_fullZoomReset", 1.461 + }, { 1.462 + id: "zoom-in-button", 1.463 + command: "cmd_fullZoomEnlarge", 1.464 + label: true, 1.465 + tooltiptext: "tooltiptext2", 1.466 + shortcutId: "key_fullZoomEnlarge", 1.467 + }]; 1.468 + 1.469 + let node = aDocument.createElementNS(kNSXUL, "toolbaritem"); 1.470 + node.setAttribute("id", "zoom-controls"); 1.471 + node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label")); 1.472 + node.setAttribute("title", CustomizableUI.getLocalizedProperty(this, "tooltiptext")); 1.473 + // Set this as an attribute in addition to the property to make sure we can style correctly. 1.474 + node.setAttribute("removable", "true"); 1.475 + node.classList.add("chromeclass-toolbar-additional"); 1.476 + node.classList.add("toolbaritem-combined-buttons"); 1.477 + node.classList.add(kWidePanelItemClass); 1.478 + 1.479 + buttons.forEach(function(aButton, aIndex) { 1.480 + if (aIndex != 0) 1.481 + node.appendChild(aDocument.createElementNS(kNSXUL, "separator")); 1.482 + let btnNode = aDocument.createElementNS(kNSXUL, "toolbarbutton"); 1.483 + setAttributes(btnNode, aButton); 1.484 + node.appendChild(btnNode); 1.485 + }); 1.486 + 1.487 + // The middle node is the 'Reset Zoom' button. 1.488 + let zoomResetButton = node.childNodes[2]; 1.489 + let window = aDocument.defaultView; 1.490 + function updateZoomResetButton() { 1.491 + let updateDisplay = true; 1.492 + // Label should always show 100% in customize mode, so don't update: 1.493 + if (aDocument.documentElement.hasAttribute("customizing")) { 1.494 + updateDisplay = false; 1.495 + } 1.496 + //XXXgijs in some tests we get called very early, and there's no docShell on the 1.497 + // tabbrowser. This breaks the zoom toolkit code (see bug 897410). Don't let that happen: 1.498 + let zoomFactor = 100; 1.499 + try { 1.500 + zoomFactor = Math.round(window.ZoomManager.zoom * 100); 1.501 + } catch (e) {} 1.502 + zoomResetButton.setAttribute("label", CustomizableUI.getLocalizedProperty( 1.503 + buttons[1], "label", [updateDisplay ? zoomFactor : 100] 1.504 + )); 1.505 + }; 1.506 + 1.507 + // Register ourselves with the service so we know when the zoom prefs change. 1.508 + Services.obs.addObserver(updateZoomResetButton, "browser-fullZoom:zoomChange", false); 1.509 + Services.obs.addObserver(updateZoomResetButton, "browser-fullZoom:zoomReset", false); 1.510 + Services.obs.addObserver(updateZoomResetButton, "browser-fullZoom:location-change", false); 1.511 + 1.512 + if (inPanel) { 1.513 + let panel = aDocument.getElementById(kPanelId); 1.514 + panel.addEventListener("popupshowing", updateZoomResetButton); 1.515 + } else { 1.516 + if (inToolbar) { 1.517 + let container = window.gBrowser.tabContainer; 1.518 + container.addEventListener("TabSelect", updateZoomResetButton); 1.519 + } 1.520 + updateZoomResetButton(); 1.521 + } 1.522 + updateCombinedWidgetStyle(node, this.currentArea, true); 1.523 + 1.524 + let listener = { 1.525 + onWidgetAdded: function(aWidgetId, aArea, aPosition) { 1.526 + if (aWidgetId != this.id) 1.527 + return; 1.528 + 1.529 + updateCombinedWidgetStyle(node, aArea, true); 1.530 + updateZoomResetButton(); 1.531 + 1.532 + let areaType = CustomizableUI.getAreaType(aArea); 1.533 + if (areaType == CustomizableUI.TYPE_MENU_PANEL) { 1.534 + let panel = aDocument.getElementById(kPanelId); 1.535 + panel.addEventListener("popupshowing", updateZoomResetButton); 1.536 + } else if (areaType == CustomizableUI.TYPE_TOOLBAR) { 1.537 + let container = window.gBrowser.tabContainer; 1.538 + container.addEventListener("TabSelect", updateZoomResetButton); 1.539 + } 1.540 + }.bind(this), 1.541 + 1.542 + onWidgetRemoved: function(aWidgetId, aPrevArea) { 1.543 + if (aWidgetId != this.id) 1.544 + return; 1.545 + 1.546 + let areaType = CustomizableUI.getAreaType(aPrevArea); 1.547 + if (areaType == CustomizableUI.TYPE_MENU_PANEL) { 1.548 + let panel = aDocument.getElementById(kPanelId); 1.549 + panel.removeEventListener("popupshowing", updateZoomResetButton); 1.550 + } else if (areaType == CustomizableUI.TYPE_TOOLBAR) { 1.551 + let container = window.gBrowser.tabContainer; 1.552 + container.removeEventListener("TabSelect", updateZoomResetButton); 1.553 + } 1.554 + 1.555 + // When a widget is demoted to the palette ('removed'), it's visual 1.556 + // style should change. 1.557 + updateCombinedWidgetStyle(node, null, true); 1.558 + updateZoomResetButton(); 1.559 + }.bind(this), 1.560 + 1.561 + onWidgetReset: function(aWidgetNode) { 1.562 + if (aWidgetNode != node) 1.563 + return; 1.564 + updateCombinedWidgetStyle(node, this.currentArea, true); 1.565 + updateZoomResetButton(); 1.566 + }.bind(this), 1.567 + 1.568 + onWidgetMoved: function(aWidgetId, aArea) { 1.569 + if (aWidgetId != this.id) 1.570 + return; 1.571 + updateCombinedWidgetStyle(node, aArea, true); 1.572 + updateZoomResetButton(); 1.573 + }.bind(this), 1.574 + 1.575 + onWidgetInstanceRemoved: function(aWidgetId, aDoc) { 1.576 + if (aWidgetId != this.id || aDoc != aDocument) 1.577 + return; 1.578 + 1.579 + CustomizableUI.removeListener(listener); 1.580 + Services.obs.removeObserver(updateZoomResetButton, "browser-fullZoom:zoomChange"); 1.581 + Services.obs.removeObserver(updateZoomResetButton, "browser-fullZoom:zoomReset"); 1.582 + Services.obs.removeObserver(updateZoomResetButton, "browser-fullZoom:location-change"); 1.583 + let panel = aDoc.getElementById(kPanelId); 1.584 + panel.removeEventListener("popupshowing", updateZoomResetButton); 1.585 + let container = aDoc.defaultView.gBrowser.tabContainer; 1.586 + container.removeEventListener("TabSelect", updateZoomResetButton); 1.587 + }.bind(this), 1.588 + 1.589 + onCustomizeStart: function(aWindow) { 1.590 + if (aWindow.document == aDocument) { 1.591 + updateZoomResetButton(); 1.592 + } 1.593 + }, 1.594 + 1.595 + onCustomizeEnd: function(aWindow) { 1.596 + if (aWindow.document == aDocument) { 1.597 + updateZoomResetButton(); 1.598 + } 1.599 + }, 1.600 + 1.601 + onWidgetDrag: function(aWidgetId, aArea) { 1.602 + if (aWidgetId != this.id) 1.603 + return; 1.604 + aArea = aArea || this.currentArea; 1.605 + updateCombinedWidgetStyle(node, aArea, true); 1.606 + }.bind(this) 1.607 + }; 1.608 + CustomizableUI.addListener(listener); 1.609 + 1.610 + return node; 1.611 + } 1.612 + }, { 1.613 + id: "edit-controls", 1.614 + type: "custom", 1.615 + tooltiptext: "edit-controls.tooltiptext2", 1.616 + defaultArea: CustomizableUI.AREA_PANEL, 1.617 + onBuild: function(aDocument) { 1.618 + let buttons = [{ 1.619 + id: "cut-button", 1.620 + command: "cmd_cut", 1.621 + label: true, 1.622 + tooltiptext: "tooltiptext2", 1.623 + shortcutId: "key_cut", 1.624 + }, { 1.625 + id: "copy-button", 1.626 + command: "cmd_copy", 1.627 + label: true, 1.628 + tooltiptext: "tooltiptext2", 1.629 + shortcutId: "key_copy", 1.630 + }, { 1.631 + id: "paste-button", 1.632 + command: "cmd_paste", 1.633 + label: true, 1.634 + tooltiptext: "tooltiptext2", 1.635 + shortcutId: "key_paste", 1.636 + }]; 1.637 + 1.638 + let node = aDocument.createElementNS(kNSXUL, "toolbaritem"); 1.639 + node.setAttribute("id", "edit-controls"); 1.640 + node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label")); 1.641 + node.setAttribute("title", CustomizableUI.getLocalizedProperty(this, "tooltiptext")); 1.642 + // Set this as an attribute in addition to the property to make sure we can style correctly. 1.643 + node.setAttribute("removable", "true"); 1.644 + node.classList.add("chromeclass-toolbar-additional"); 1.645 + node.classList.add("toolbaritem-combined-buttons"); 1.646 + node.classList.add(kWidePanelItemClass); 1.647 + 1.648 + buttons.forEach(function(aButton, aIndex) { 1.649 + if (aIndex != 0) 1.650 + node.appendChild(aDocument.createElementNS(kNSXUL, "separator")); 1.651 + let btnNode = aDocument.createElementNS(kNSXUL, "toolbarbutton"); 1.652 + setAttributes(btnNode, aButton); 1.653 + node.appendChild(btnNode); 1.654 + }); 1.655 + 1.656 + updateCombinedWidgetStyle(node, this.currentArea); 1.657 + 1.658 + let listener = { 1.659 + onWidgetAdded: function(aWidgetId, aArea, aPosition) { 1.660 + if (aWidgetId != this.id) 1.661 + return; 1.662 + updateCombinedWidgetStyle(node, aArea); 1.663 + }.bind(this), 1.664 + 1.665 + onWidgetRemoved: function(aWidgetId, aPrevArea) { 1.666 + if (aWidgetId != this.id) 1.667 + return; 1.668 + // When a widget is demoted to the palette ('removed'), it's visual 1.669 + // style should change. 1.670 + updateCombinedWidgetStyle(node); 1.671 + }.bind(this), 1.672 + 1.673 + onWidgetReset: function(aWidgetNode) { 1.674 + if (aWidgetNode != node) 1.675 + return; 1.676 + updateCombinedWidgetStyle(node, this.currentArea); 1.677 + }.bind(this), 1.678 + 1.679 + onWidgetMoved: function(aWidgetId, aArea) { 1.680 + if (aWidgetId != this.id) 1.681 + return; 1.682 + updateCombinedWidgetStyle(node, aArea); 1.683 + }.bind(this), 1.684 + 1.685 + onWidgetInstanceRemoved: function(aWidgetId, aDoc) { 1.686 + if (aWidgetId != this.id || aDoc != aDocument) 1.687 + return; 1.688 + CustomizableUI.removeListener(listener); 1.689 + }.bind(this), 1.690 + 1.691 + onWidgetDrag: function(aWidgetId, aArea) { 1.692 + if (aWidgetId != this.id) 1.693 + return; 1.694 + aArea = aArea || this.currentArea; 1.695 + updateCombinedWidgetStyle(node, aArea); 1.696 + }.bind(this) 1.697 + }; 1.698 + CustomizableUI.addListener(listener); 1.699 + 1.700 + return node; 1.701 + } 1.702 + }, 1.703 + { 1.704 + id: "feed-button", 1.705 + type: "view", 1.706 + viewId: "PanelUI-feeds", 1.707 + tooltiptext: "feed-button.tooltiptext2", 1.708 + defaultArea: CustomizableUI.AREA_PANEL, 1.709 + onClick: function(aEvent) { 1.710 + let win = aEvent.target.ownerDocument.defaultView; 1.711 + let feeds = win.gBrowser.selectedBrowser.feeds; 1.712 + 1.713 + // Here, we only care about the case where we have exactly 1 feed and the 1.714 + // user clicked... 1.715 + let isClick = (aEvent.button == 0 || aEvent.button == 1); 1.716 + if (feeds && feeds.length == 1 && isClick) { 1.717 + aEvent.preventDefault(); 1.718 + aEvent.stopPropagation(); 1.719 + win.FeedHandler.subscribeToFeed(feeds[0].href, aEvent); 1.720 + CustomizableUI.hidePanelForNode(aEvent.target); 1.721 + } 1.722 + }, 1.723 + onViewShowing: function(aEvent) { 1.724 + let doc = aEvent.detail.ownerDocument; 1.725 + let container = doc.getElementById("PanelUI-feeds"); 1.726 + let gotView = doc.defaultView.FeedHandler.buildFeedList(container, true); 1.727 + 1.728 + // For no feeds or only a single one, don't show the panel. 1.729 + if (!gotView) { 1.730 + aEvent.preventDefault(); 1.731 + aEvent.stopPropagation(); 1.732 + return; 1.733 + } 1.734 + }, 1.735 + onCreated: function(node) { 1.736 + let win = node.ownerDocument.defaultView; 1.737 + let selectedBrowser = win.gBrowser.selectedBrowser; 1.738 + let feeds = selectedBrowser && selectedBrowser.feeds; 1.739 + if (!feeds || !feeds.length) { 1.740 + node.setAttribute("disabled", "true"); 1.741 + } 1.742 + } 1.743 + }, { 1.744 + id: "characterencoding-button", 1.745 + type: "view", 1.746 + viewId: "PanelUI-characterEncodingView", 1.747 + tooltiptext: "characterencoding-button.tooltiptext2", 1.748 + defaultArea: CustomizableUI.AREA_PANEL, 1.749 + maybeDisableMenu: function(aDocument) { 1.750 + let window = aDocument.defaultView; 1.751 + return !(window.gBrowser && 1.752 + window.gBrowser.docShell && 1.753 + window.gBrowser.docShell.mayEnableCharacterEncodingMenu); 1.754 + }, 1.755 + populateList: function(aDocument, aContainerId, aSection) { 1.756 + let containerElem = aDocument.getElementById(aContainerId); 1.757 + 1.758 + containerElem.addEventListener("command", this.onCommand, false); 1.759 + 1.760 + let list = this.charsetInfo[aSection]; 1.761 + 1.762 + for (let item of list) { 1.763 + let elem = aDocument.createElementNS(kNSXUL, "toolbarbutton"); 1.764 + elem.setAttribute("label", item.label); 1.765 + elem.setAttribute("type", "checkbox"); 1.766 + elem.section = aSection; 1.767 + elem.value = item.value; 1.768 + elem.setAttribute("class", "subviewbutton"); 1.769 + containerElem.appendChild(elem); 1.770 + } 1.771 + }, 1.772 + updateCurrentCharset: function(aDocument) { 1.773 + let content = aDocument.defaultView.content; 1.774 + let currentCharset = content && content.document && content.document.characterSet; 1.775 + currentCharset = CharsetMenu.foldCharset(currentCharset); 1.776 + 1.777 + let pinnedContainer = aDocument.getElementById("PanelUI-characterEncodingView-pinned"); 1.778 + let charsetContainer = aDocument.getElementById("PanelUI-characterEncodingView-charsets"); 1.779 + let elements = [...(pinnedContainer.childNodes), ...(charsetContainer.childNodes)]; 1.780 + 1.781 + this._updateElements(elements, currentCharset); 1.782 + }, 1.783 + updateCurrentDetector: function(aDocument) { 1.784 + let detectorContainer = aDocument.getElementById("PanelUI-characterEncodingView-autodetect"); 1.785 + let currentDetector; 1.786 + try { 1.787 + currentDetector = Services.prefs.getComplexValue( 1.788 + "intl.charset.detector", Ci.nsIPrefLocalizedString).data; 1.789 + } catch (e) {} 1.790 + 1.791 + this._updateElements(detectorContainer.childNodes, currentDetector); 1.792 + }, 1.793 + _updateElements: function(aElements, aCurrentItem) { 1.794 + if (!aElements.length) { 1.795 + return; 1.796 + } 1.797 + let disabled = this.maybeDisableMenu(aElements[0].ownerDocument); 1.798 + for (let elem of aElements) { 1.799 + if (disabled) { 1.800 + elem.setAttribute("disabled", "true"); 1.801 + } else { 1.802 + elem.removeAttribute("disabled"); 1.803 + } 1.804 + if (elem.value.toLowerCase() == aCurrentItem.toLowerCase()) { 1.805 + elem.setAttribute("checked", "true"); 1.806 + } else { 1.807 + elem.removeAttribute("checked"); 1.808 + } 1.809 + } 1.810 + }, 1.811 + onViewShowing: function(aEvent) { 1.812 + let document = aEvent.target.ownerDocument; 1.813 + 1.814 + let autoDetectLabelId = "PanelUI-characterEncodingView-autodetect-label"; 1.815 + let autoDetectLabel = document.getElementById(autoDetectLabelId); 1.816 + if (!autoDetectLabel.hasAttribute("value")) { 1.817 + let label = CharsetBundle.GetStringFromName("charsetMenuAutodet"); 1.818 + autoDetectLabel.setAttribute("value", label); 1.819 + this.populateList(document, 1.820 + "PanelUI-characterEncodingView-pinned", 1.821 + "pinnedCharsets"); 1.822 + this.populateList(document, 1.823 + "PanelUI-characterEncodingView-charsets", 1.824 + "otherCharsets"); 1.825 + this.populateList(document, 1.826 + "PanelUI-characterEncodingView-autodetect", 1.827 + "detectors"); 1.828 + } 1.829 + this.updateCurrentDetector(document); 1.830 + this.updateCurrentCharset(document); 1.831 + }, 1.832 + onCommand: function(aEvent) { 1.833 + let node = aEvent.target; 1.834 + if (!node.hasAttribute || !node.section) { 1.835 + return; 1.836 + } 1.837 + 1.838 + let window = node.ownerDocument.defaultView; 1.839 + let section = node.section; 1.840 + let value = node.value; 1.841 + 1.842 + // The behavior as implemented here is directly based off of the 1.843 + // `MultiplexHandler()` method in browser.js. 1.844 + if (section != "detectors") { 1.845 + window.BrowserSetForcedCharacterSet(value); 1.846 + } else { 1.847 + // Set the detector pref. 1.848 + try { 1.849 + let str = Cc["@mozilla.org/supports-string;1"] 1.850 + .createInstance(Ci.nsISupportsString); 1.851 + str.data = value; 1.852 + Services.prefs.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str); 1.853 + } catch (e) { 1.854 + Cu.reportError("Failed to set the intl.charset.detector preference."); 1.855 + } 1.856 + // Prepare a browser page reload with a changed charset. 1.857 + window.BrowserCharsetReload(); 1.858 + } 1.859 + }, 1.860 + onCreated: function(aNode) { 1.861 + const kPanelId = "PanelUI-popup"; 1.862 + let document = aNode.ownerDocument; 1.863 + 1.864 + let updateButton = () => { 1.865 + if (this.maybeDisableMenu(document)) 1.866 + aNode.setAttribute("disabled", "true"); 1.867 + else 1.868 + aNode.removeAttribute("disabled"); 1.869 + }; 1.870 + 1.871 + if (this.currentArea == CustomizableUI.AREA_PANEL) { 1.872 + let panel = document.getElementById(kPanelId); 1.873 + panel.addEventListener("popupshowing", updateButton); 1.874 + } 1.875 + 1.876 + let listener = { 1.877 + onWidgetAdded: (aWidgetId, aArea) => { 1.878 + if (aWidgetId != this.id) 1.879 + return; 1.880 + if (aArea == CustomizableUI.AREA_PANEL) { 1.881 + let panel = document.getElementById(kPanelId); 1.882 + panel.addEventListener("popupshowing", updateButton); 1.883 + } 1.884 + }, 1.885 + onWidgetRemoved: (aWidgetId, aPrevArea) => { 1.886 + if (aWidgetId != this.id) 1.887 + return; 1.888 + aNode.removeAttribute("disabled"); 1.889 + if (aPrevArea == CustomizableUI.AREA_PANEL) { 1.890 + let panel = document.getElementById(kPanelId); 1.891 + panel.removeEventListener("popupshowing", updateButton); 1.892 + } 1.893 + }, 1.894 + onWidgetInstanceRemoved: (aWidgetId, aDoc) => { 1.895 + if (aWidgetId != this.id || aDoc != document) 1.896 + return; 1.897 + 1.898 + CustomizableUI.removeListener(listener); 1.899 + let panel = aDoc.getElementById(kPanelId); 1.900 + panel.removeEventListener("popupshowing", updateButton); 1.901 + } 1.902 + }; 1.903 + CustomizableUI.addListener(listener); 1.904 + if (!this.charsetInfo) { 1.905 + this.charsetInfo = CharsetMenu.getData(); 1.906 + } 1.907 + } 1.908 + }, { 1.909 + id: "email-link-button", 1.910 + tooltiptext: "email-link-button.tooltiptext3", 1.911 + onCommand: function(aEvent) { 1.912 + let win = aEvent.view; 1.913 + win.MailIntegration.sendLinkForWindow(win.content); 1.914 + } 1.915 + }]; 1.916 + 1.917 +#ifdef XP_WIN 1.918 +#ifdef MOZ_METRO 1.919 +if (Services.metro && Services.metro.supported) { 1.920 + let widgetArgs = {tooltiptext: "switch-to-metro-button2.tooltiptext"}; 1.921 + let brandShortName = BrandBundle.GetStringFromName("brandShortName"); 1.922 + let metroTooltip = CustomizableUI.getLocalizedProperty(widgetArgs, "tooltiptext", 1.923 + [brandShortName]); 1.924 + CustomizableWidgets.push({ 1.925 + id: "switch-to-metro-button", 1.926 + label: "switch-to-metro-button2.label", 1.927 + tooltiptext: metroTooltip, 1.928 + defaultArea: CustomizableUI.AREA_PANEL, 1.929 + showInPrivateBrowsing: false, /* See bug 928068 */ 1.930 + onCommand: function(aEvent) { 1.931 + let win = aEvent.view; 1.932 + if (win && typeof win.SwitchToMetro == "function") { 1.933 + win.SwitchToMetro(); 1.934 + } 1.935 + } 1.936 + }); 1.937 +} 1.938 +#endif 1.939 +#endif 1.940 + 1.941 +#ifdef NIGHTLY_BUILD 1.942 +/** 1.943 + * The e10s button's purpose is to lower the barrier of entry 1.944 + * for our Nightly testers to use e10s windows. We'll be removing it 1.945 + * once remote tabs are enabled. This button should never ever make it 1.946 + * to production. If it does, that'd be bad, and we should all feel bad. 1.947 + */ 1.948 +if (Services.prefs.getBoolPref("browser.tabs.remote")) { 1.949 + let getCommandFunction = function(aOpenRemote) { 1.950 + return function(aEvent) { 1.951 + let win = aEvent.view; 1.952 + if (win && typeof win.OpenBrowserWindow == "function") { 1.953 + win.OpenBrowserWindow({remote: aOpenRemote}); 1.954 + } 1.955 + }; 1.956 + } 1.957 + 1.958 + let openRemote = !Services.prefs.getBoolPref("browser.tabs.remote.autostart"); 1.959 + // Like the XUL menuitem counterparts, we hard-code these strings in because 1.960 + // this button should never roll into production. 1.961 + let buttonLabel = openRemote ? "New e10s Window" 1.962 + : "New Non-e10s Window"; 1.963 + 1.964 + CustomizableWidgets.push({ 1.965 + id: "e10s-button", 1.966 + label: buttonLabel, 1.967 + tooltiptext: buttonLabel, 1.968 + defaultArea: CustomizableUI.AREA_PANEL, 1.969 + onCommand: getCommandFunction(openRemote), 1.970 + }); 1.971 +} 1.972 +#endif