diff --git a/navigator/comm/content/utilityOverlay.js b/navigator/comm/content/utilityOverlay.js
index 4284b717..c30ae4e7 100644
--- a/navigator/comm/content/utilityOverlay.js
+++ b/navigator/comm/content/utilityOverlay.js
@@ -267,8 +267,6 @@ function goToggleToolbar(id, elementID)
}
-var gCustomizeSheet = false;
-
function SuiteCustomizeToolbar(aMenuItem)
{
let toolbar = aMenuItem.parentNode.triggerNode;
@@ -288,41 +286,10 @@ function goCustomizeToolbar(toolbox)
if ("customizeInit" in toolbox)
toolbox.customizeInit();
- var customizeURL = "chrome://global/content/customizeToolbar.xul";
-
- gCustomizeSheet = GetBoolPref("toolbar.customization.usesheet", false);
-
- if (gCustomizeSheet) {
- var sheetFrame = document.getElementById("customizeToolbarSheetIFrame");
- var panel = document.getElementById("customizeToolbarSheetPopup");
- sheetFrame.hidden = false;
- sheetFrame.toolbox = toolbox;
- sheetFrame.panel = panel;
-
- // The document might not have been loaded yet, if this is the first time.
- // If it is already loaded, reload it so that the onload initialization
- // code re-runs.
- if (sheetFrame.getAttribute("src") == customizeURL)
- sheetFrame.contentWindow.location.reload();
- else
- sheetFrame.setAttribute("src", customizeURL);
-
- // Open the panel, but make it invisible until the iframe has loaded so
- // that the user doesn't see a white flash.
- panel.style.visibility = "hidden";
- toolbox.addEventListener("beforecustomization", function toolboxBeforeCustom() {
- toolbox.removeEventListener("beforecustomization", toolboxBeforeCustom, false);
- panel.style.removeProperty("visibility");
- }, false);
- panel.openPopup(toolbox, "after_start", 0, 0);
- return sheetFrame.contentWindow;
- }
- else {
- return window.openDialog(customizeURL,
- "",
- "chrome,all,dependent",
- toolbox);
- }
+ return window.openDialog("chrome://global/content/customizeToolbar.xul",
+ "",
+ "chrome,all,dependent,centerscreen",
+ toolbox);
}
function onViewToolbarsPopupShowing(aEvent, aInsertPoint)
@@ -521,14 +488,7 @@ function toolboxCustomizeInit(menubarID)
function toolboxCustomizeDone(menubarID, toolbox, aToolboxChanged)
{
- if (gCustomizeSheet) {
- document.getElementById("customizeToolbarSheetIFrame").hidden = true;
- document.getElementById("customizeToolbarSheetPopup").hidePopup();
- if (content)
- content.focus();
- else
- window.focus();
- }
+ window.focus();
// Re-enable parts of the UI we disabled during the dialog
var menubar = document.getElementById(menubarID);
diff --git a/navigator/components/moz.build b/navigator/components/moz.build
index 70961fbe..dce6826f 100644
--- a/navigator/components/moz.build
+++ b/navigator/components/moz.build
@@ -19,6 +19,7 @@ DIRS += [
'shell',
'sidebar',
'smile',
+ 'toolbar',
]
XPIDL_SOURCES += ['nsINavigatorGlue.idl']
diff --git a/navigator/components/toolbar/content/customizeToolbar.css b/navigator/components/toolbar/content/customizeToolbar.css
new file mode 100644
index 00000000..5b3b1474
--- /dev/null
+++ b/navigator/components/toolbar/content/customizeToolbar.css
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */
+@namespace html url("http://www.w3.org/1999/xhtml"); /* namespace for HTML elements */
+
+#main-box > box {
+ overflow: hidden;
+ margin-top: 9px;
+ margin-right: 8px;
+ margin-bottom: 5px;
+ margin-left: 8px;
+}
+
+#palette-box {
+ -moz-appearance: listbox;
+ overflow: auto;
+ display: block;
+ min-height: 3em;
+ margin-top: 8px;
+ margin-right: 8px;
+ margin-left: 8px;
+}
+
+#palette-box > toolbarpaletteitem {
+ width: 110px;
+ height: 94px;
+ overflow: hidden;
+ display: inline-block;
+ padding: 8px 2px;
+ margin: 0 8px;
+}
+
+#palette-box > toolbarpaletteitem > label {
+ text-align: center;
+}
+
+.toolbarpaletteitem-box {
+ -moz-box-pack: center;
+ -moz-box-flex: 1;
+ width: 110px;
+ max-width: 110px;
+}
+
+/* Hide the toolbarbutton label because we replicate it on the wrapper */
+.toolbarbutton-text {
+ display: none;
+}
+
+separator {
+ margin-left: 8px;
+ margin-right: 8px;
+}
+
+.actionButtons {
+ margin: 0 3px 6px !important;
+}
\ No newline at end of file
diff --git a/navigator/components/toolbar/content/customizeToolbar.js b/navigator/components/toolbar/content/customizeToolbar.js
new file mode 100644
index 00000000..73d6cff5
--- /dev/null
+++ b/navigator/components/toolbar/content/customizeToolbar.js
@@ -0,0 +1,832 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const gToolbarInfoSeparators = ["|", "-"];
+
+var gToolboxDocument = null;
+var gToolbox = null;
+var gCurrentDragOverItem = null;
+var gToolboxChanged = false;
+var gToolboxSheet = false;
+var gPaletteBox = null;
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function onLoad()
+{
+ if ("arguments" in window && window.arguments[0]) {
+ InitWithToolbox(window.arguments[0]);
+ repositionDialog(window);
+ }
+ else if (window.frameElement &&
+ "toolbox" in window.frameElement) {
+ gToolboxSheet = true;
+ InitWithToolbox(window.frameElement.toolbox);
+ repositionDialog(window.frameElement.panel);
+ }
+}
+
+function InitWithToolbox(aToolbox)
+{
+ gToolbox = aToolbox;
+ dispatchCustomizationEvent("beforecustomization");
+ gToolboxDocument = gToolbox.ownerDocument;
+ gToolbox.customizing = true;
+ forEachCustomizableToolbar(function (toolbar) {
+ toolbar.setAttribute("customizing", "true");
+ });
+ gPaletteBox = document.getElementById("palette-box");
+
+ var elts = getRootElements();
+ for (let i=0; i < elts.length; i++) {
+ elts[i].addEventListener("dragstart", onToolbarDragStart, true);
+ elts[i].addEventListener("dragover", onToolbarDragOver, true);
+ elts[i].addEventListener("dragexit", onToolbarDragExit, true);
+ elts[i].addEventListener("drop", onToolbarDrop, true);
+ }
+
+ initDialog();
+}
+
+function onClose()
+{
+ if (!gToolboxSheet)
+ window.close();
+ else
+ finishToolbarCustomization();
+}
+
+function onUnload()
+{
+ if (!gToolboxSheet)
+ finishToolbarCustomization();
+}
+
+function finishToolbarCustomization()
+{
+ removeToolboxListeners();
+ unwrapToolbarItems();
+ persistCurrentSets();
+ gToolbox.customizing = false;
+ forEachCustomizableToolbar(function (toolbar) {
+ toolbar.removeAttribute("customizing");
+ });
+
+ notifyParentComplete();
+}
+
+function initDialog()
+{
+ if (!gToolbox.toolbarset) {
+ document.getElementById("newtoolbar").hidden = true;
+ }
+
+ // Build up the palette of other items.
+ buildPalette();
+
+ // Wrap all the items on the toolbar in toolbarpaletteitems.
+ wrapToolbarItems();
+}
+
+function repositionDialog(aWindow)
+{
+ // Position the dialog touching the bottom of the toolbox and centered with
+ // it.
+ if (!aWindow)
+ return;
+
+ var width;
+ if (aWindow != window)
+ width = aWindow.getBoundingClientRect().width;
+ else if (document.documentElement.hasAttribute("width"))
+ width = document.documentElement.getAttribute("width");
+ else
+ width = parseInt(document.documentElement.style.width);
+ var screenX = gToolbox.boxObject.screenX
+ + ((gToolbox.boxObject.width - width) / 2);
+ var screenY = gToolbox.boxObject.screenY + gToolbox.boxObject.height;
+
+ aWindow.moveTo(screenX, screenY);
+}
+
+function removeToolboxListeners()
+{
+ var elts = getRootElements();
+ for (let i=0; i < elts.length; i++) {
+ elts[i].removeEventListener("dragstart", onToolbarDragStart, true);
+ elts[i].removeEventListener("dragover", onToolbarDragOver, true);
+ elts[i].removeEventListener("dragexit", onToolbarDragExit, true);
+ elts[i].removeEventListener("drop", onToolbarDrop, true);
+ }
+}
+
+/**
+ * Invoke a callback on the toolbox to notify it that the dialog is done
+ * and going away.
+ */
+function notifyParentComplete()
+{
+ if ("customizeDone" in gToolbox)
+ gToolbox.customizeDone(gToolboxChanged);
+ dispatchCustomizationEvent("aftercustomization");
+}
+
+function toolboxChanged(aType)
+{
+ gToolboxChanged = true;
+ if ("customizeChange" in gToolbox)
+ gToolbox.customizeChange(aType);
+ dispatchCustomizationEvent("customizationchange");
+}
+
+function dispatchCustomizationEvent(aEventName) {
+ var evt = document.createEvent("Events");
+ evt.initEvent(aEventName, true, true);
+ gToolbox.dispatchEvent(evt);
+}
+
+/**
+ * Persist the current set of buttons in all customizable toolbars to
+ * localstore.
+ */
+function persistCurrentSets()
+{
+ if (!gToolboxChanged || gToolboxDocument.defaultView.closed)
+ return;
+
+ var customCount = 0;
+ forEachCustomizableToolbar(function (toolbar) {
+ // Calculate currentset and store it in the attribute.
+ var currentSet = toolbar.currentSet;
+ toolbar.setAttribute("currentset", currentSet);
+
+ var customIndex = toolbar.hasAttribute("customindex");
+ if (customIndex) {
+ if (!toolbar.hasChildNodes()) {
+ // Remove custom toolbars whose contents have been removed.
+ gToolbox.removeChild(toolbar);
+ } else if (gToolbox.toolbarset) {
+ var hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
+ "autohide" : "collapsed";
+ // Persist custom toolbar info on the
+ // Attributes:
+ // Names: "toolbarX" (X - the number of the toolbar)
+ // Values: "Name|HidingAttributeName-HidingAttributeValue|CurrentSet"
+ gToolbox.toolbarset.setAttribute("toolbar" + (++customCount),
+ toolbar.toolbarName
+ + gToolbarInfoSeparators[0]
+ + hidingAttribute
+ + gToolbarInfoSeparators[1]
+ + toolbar.getAttribute(hidingAttribute)
+ + gToolbarInfoSeparators[0]
+ + currentSet);
+ gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
+ }
+ }
+
+ if (!customIndex) {
+ // Persist the currentset attribute directly on hardcoded toolbars.
+ gToolboxDocument.persist(toolbar.id, "currentset");
+ }
+ });
+
+ // Remove toolbarX attributes for removed toolbars.
+ while (gToolbox.toolbarset && gToolbox.toolbarset.hasAttribute("toolbar"+(++customCount))) {
+ gToolbox.toolbarset.removeAttribute("toolbar"+customCount);
+ gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
+ }
+}
+
+/**
+ * Wraps all items in all customizable toolbars in a toolbox.
+ */
+function wrapToolbarItems()
+{
+ forEachCustomizableToolbar(function (toolbar) {
+ Array.forEach(toolbar.childNodes, function (item) {
+#ifdef XP_MACOSX
+ if (item.firstChild && item.firstChild.localName == "menubar")
+ return;
+#endif
+ if (isToolbarItem(item)) {
+ let wrapper = wrapToolbarItem(item);
+ cleanupItemForToolbar(item, wrapper);
+ }
+ });
+ });
+}
+
+function getRootElements()
+{
+ return [gToolbox].concat(gToolbox.externalToolbars);
+}
+
+/**
+ * Unwraps all items in all customizable toolbars in a toolbox.
+ */
+function unwrapToolbarItems()
+{
+ let elts = getRootElements();
+ for (let i=0; i < elts.length; i++) {
+ let paletteItems = elts[i].getElementsByTagName("toolbarpaletteitem");
+ let paletteItem;
+ while ((paletteItem = paletteItems.item(0)) != null) {
+ let toolbarItem = paletteItem.firstChild;
+ restoreItemForToolbar(toolbarItem, paletteItem);
+ paletteItem.parentNode.replaceChild(toolbarItem, paletteItem);
+ }
+ }
+}
+
+/**
+ * Creates a wrapper that can be used to contain a toolbaritem and prevent
+ * it from receiving UI events.
+ */
+function createWrapper(aId, aDocument)
+{
+ var wrapper = aDocument.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "toolbarpaletteitem");
+
+ wrapper.id = "wrapper-"+aId;
+ return wrapper;
+}
+
+/**
+ * Wraps an item that has been cloned from a template and adds
+ * it to the end of the palette.
+ */
+function wrapPaletteItem(aPaletteItem)
+{
+ var wrapper = createWrapper(aPaletteItem.id, document);
+
+ wrapper.appendChild(aPaletteItem);
+
+ // XXX We need to call this AFTER the palette item has been appended
+ // to the wrapper or else we crash dropping certain buttons on the
+ // palette due to removal of the command and disabled attributes - JRH
+ cleanUpItemForPalette(aPaletteItem, wrapper);
+
+ gPaletteBox.appendChild(wrapper);
+}
+
+/**
+ * Wraps an item that is currently on a toolbar and replaces the item
+ * with the wrapper. This is not used when dropping items from the palette,
+ * only when first starting the dialog and wrapping everything on the toolbars.
+ */
+function wrapToolbarItem(aToolbarItem)
+{
+ var wrapper = createWrapper(aToolbarItem.id, gToolboxDocument);
+
+ wrapper.flex = aToolbarItem.flex;
+
+ aToolbarItem.parentNode.replaceChild(wrapper, aToolbarItem);
+
+ wrapper.appendChild(aToolbarItem);
+
+ return wrapper;
+}
+
+/**
+ * Get the list of ids for the current set of items on each toolbar.
+ */
+function getCurrentItemIds()
+{
+ var currentItems = {};
+ forEachCustomizableToolbar(function (toolbar) {
+ var child = toolbar.firstChild;
+ while (child) {
+ if (isToolbarItem(child))
+ currentItems[child.id] = 1;
+ child = child.nextSibling;
+ }
+ });
+ return currentItems;
+}
+
+/**
+ * Builds the palette of draggable items that are not yet in a toolbar.
+ */
+function buildPalette()
+{
+ // Empty the palette first.
+ while (gPaletteBox.lastChild)
+ gPaletteBox.removeChild(gPaletteBox.lastChild);
+
+ // Add the toolbar separator item.
+ var templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "toolbarseparator");
+ templateNode.id = "separator";
+ wrapPaletteItem(templateNode);
+
+ // Add the toolbar spring item.
+ templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "toolbarspring");
+ templateNode.id = "spring";
+ templateNode.flex = 1;
+ wrapPaletteItem(templateNode);
+
+ // Add the toolbar spacer item.
+ templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "toolbarspacer");
+ templateNode.id = "spacer";
+ templateNode.flex = 1;
+ wrapPaletteItem(templateNode);
+
+ var currentItems = getCurrentItemIds();
+ templateNode = gToolbox.palette.firstChild;
+ while (templateNode) {
+ // Check if the item is already in a toolbar before adding it to the palette.
+ if (!(templateNode.id in currentItems)) {
+ var paletteItem = document.importNode(templateNode, true);
+ wrapPaletteItem(paletteItem);
+ }
+
+ templateNode = templateNode.nextSibling;
+ }
+}
+
+/**
+ * Makes sure that an item that has been cloned from a template
+ * is stripped of any attributes that may adversely affect its
+ * appearance in the palette.
+ */
+function cleanUpItemForPalette(aItem, aWrapper)
+{
+ aWrapper.setAttribute("place", "palette");
+ setWrapperType(aItem, aWrapper);
+
+ if (aItem.hasAttribute("title"))
+ aWrapper.setAttribute("title", aItem.getAttribute("title"));
+ else if (aItem.hasAttribute("label"))
+ aWrapper.setAttribute("title", aItem.getAttribute("label"));
+ else if (isSpecialItem(aItem)) {
+ var stringBundle = document.getElementById("stringBundle");
+ // Remove the common "toolbar" prefix to generate the string name.
+ var title = stringBundle.getString(aItem.localName.slice(7) + "Title");
+ aWrapper.setAttribute("title", title);
+ }
+ aWrapper.setAttribute("tooltiptext", aWrapper.getAttribute("title"));
+
+ // Remove attributes that screw up our appearance.
+ aItem.removeAttribute("command");
+ aItem.removeAttribute("observes");
+ aItem.removeAttribute("type");
+ aItem.removeAttribute("width");
+
+ Array.forEach(aWrapper.querySelectorAll("[disabled]"), function(aNode) {
+ aNode.removeAttribute("disabled");
+ });
+}
+
+/**
+ * Makes sure that an item that has been cloned from a template
+ * is stripped of all properties that may adversely affect its
+ * appearance in the toolbar. Store critical properties on the
+ * wrapper so they can be put back on the item when we're done.
+ */
+function cleanupItemForToolbar(aItem, aWrapper)
+{
+ setWrapperType(aItem, aWrapper);
+ aWrapper.setAttribute("place", "toolbar");
+
+ if (aItem.hasAttribute("command")) {
+ aWrapper.setAttribute("itemcommand", aItem.getAttribute("command"));
+ aItem.removeAttribute("command");
+ }
+
+ if (aItem.checked) {
+ aWrapper.setAttribute("itemchecked", "true");
+ aItem.checked = false;
+ }
+
+ if (aItem.disabled) {
+ aWrapper.setAttribute("itemdisabled", "true");
+ aItem.disabled = false;
+ }
+}
+
+/**
+ * Restore all the properties that we stripped off above.
+ */
+function restoreItemForToolbar(aItem, aWrapper)
+{
+ if (aWrapper.hasAttribute("itemdisabled"))
+ aItem.disabled = true;
+
+ if (aWrapper.hasAttribute("itemchecked"))
+ aItem.checked = true;
+
+ if (aWrapper.hasAttribute("itemcommand")) {
+ let commandID = aWrapper.getAttribute("itemcommand");
+ aItem.setAttribute("command", commandID);
+
+ // XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing
+ let command = gToolboxDocument.getElementById(commandID);
+ if (command && command.hasAttribute("disabled"))
+ aItem.setAttribute("disabled", command.getAttribute("disabled"));
+ }
+}
+
+function setWrapperType(aItem, aWrapper)
+{
+ if (aItem.localName == "toolbarseparator") {
+ aWrapper.setAttribute("type", "separator");
+ } else if (aItem.localName == "toolbarspring") {
+ aWrapper.setAttribute("type", "spring");
+ } else if (aItem.localName == "toolbarspacer") {
+ aWrapper.setAttribute("type", "spacer");
+ } else if (aItem.localName == "toolbaritem" && aItem.firstChild) {
+ aWrapper.setAttribute("type", aItem.firstChild.localName);
+ }
+}
+
+function setDragActive(aItem, aValue)
+{
+ var node = aItem;
+ var direction = window.getComputedStyle(aItem, null).direction;
+ var value = direction == "ltr"? "left" : "right";
+ if (aItem.localName == "toolbar") {
+ node = aItem.lastChild;
+ value = direction == "ltr"? "right" : "left";
+ }
+
+ if (!node)
+ return;
+
+ if (aValue) {
+ if (!node.hasAttribute("dragover"))
+ node.setAttribute("dragover", value);
+ } else {
+ node.removeAttribute("dragover");
+ }
+}
+
+function addNewToolbar()
+{
+ var promptService = Services.prompt;
+ var stringBundle = document.getElementById("stringBundle");
+ var message = stringBundle.getString("enterToolbarName");
+ var title = stringBundle.getString("enterToolbarTitle");
+
+ var name = {};
+
+ // Quitting from the toolbar dialog while the new toolbar prompt is up
+ // can cause things to become unresponsive on the Mac. Until dialog modality
+ // is fixed (395465), disable the "Done" button explicitly.
+ var doneButton = document.getElementById("donebutton");
+ doneButton.disabled = true;
+
+ while (true) {
+
+ if (!promptService.prompt(window, title, message, name, null, {})) {
+ doneButton.disabled = false;
+ return;
+ }
+
+ if (!name.value) {
+ message = stringBundle.getFormattedString("enterToolbarBlank", [name.value]);
+ continue;
+ }
+
+ if (name.value.includes(gToolbarInfoSeparators[0])) {
+ message = stringBundle.getFormattedString("enterToolbarIllegalChars", [name.value]);
+ continue;
+ }
+
+ var dupeFound = false;
+
+ // Check for an existing toolbar with the same display name
+ for (let i = 0; i < gToolbox.childNodes.length; ++i) {
+ var toolbar = gToolbox.childNodes[i];
+ var toolbarName = toolbar.getAttribute("toolbarname");
+
+ if (toolbarName == name.value &&
+ toolbar.getAttribute("type") != "menubar" &&
+ toolbar.nodeName == 'toolbar') {
+ dupeFound = true;
+ break;
+ }
+ }
+
+ if (!dupeFound)
+ break;
+
+ message = stringBundle.getFormattedString("enterToolbarDup", [name.value]);
+ }
+
+ gToolbox.appendCustomToolbar(name.value, "", [null, null]);
+
+ toolboxChanged();
+
+ doneButton.disabled = false;
+}
+
+/**
+ * Restore the default set of buttons to fixed toolbars,
+ * remove all custom toolbars, and rebuild the palette.
+ */
+function restoreDefaultSet()
+{
+ // Unwrap the items on the toolbar.
+ unwrapToolbarItems();
+
+ // Remove all of the customized toolbars.
+ var child = gToolbox.lastChild;
+ while (child) {
+ if (child.hasAttribute("customindex")) {
+ var thisChild = child;
+ child = child.previousSibling;
+ thisChild.currentSet = "__empty";
+ gToolbox.removeChild(thisChild);
+ } else {
+ child = child.previousSibling;
+ }
+ }
+
+ // Restore the defaultset for fixed toolbars.
+ forEachCustomizableToolbar(function (toolbar) {
+ var defaultSet = toolbar.getAttribute("defaultset");
+ if (defaultSet)
+ toolbar.currentSet = defaultSet;
+ });
+
+ // Now rebuild the palette.
+ buildPalette();
+
+ // Now re-wrap the items on the toolbar.
+ wrapToolbarItems();
+
+ toolboxChanged("reset");
+}
+
+function updateToolboxProperty(aProp, aValue, aToolkitDefault) {
+ var toolboxDefault = gToolbox.getAttribute("default" + aProp) ||
+ aToolkitDefault;
+
+ gToolbox.setAttribute(aProp, aValue || toolboxDefault);
+ gToolboxDocument.persist(gToolbox.id, aProp);
+
+ forEachCustomizableToolbar(function (toolbar) {
+ var toolbarDefault = toolbar.getAttribute("default" + aProp) ||
+ toolboxDefault;
+ if (toolbar.getAttribute("lock" + aProp) == "true" &&
+ toolbar.getAttribute(aProp) == toolbarDefault)
+ return;
+
+ toolbar.setAttribute(aProp, aValue || toolbarDefault);
+ gToolboxDocument.persist(toolbar.id, aProp);
+ });
+
+ toolboxChanged(aProp);
+
+ return aValue || toolboxDefault;
+}
+
+function forEachCustomizableToolbar(callback) {
+ Array.filter(gToolbox.childNodes, isCustomizableToolbar).forEach(callback);
+ Array.filter(gToolbox.externalToolbars, isCustomizableToolbar).forEach(callback);
+}
+
+function isCustomizableToolbar(aElt)
+{
+ return aElt.localName == "toolbar" &&
+ aElt.getAttribute("customizable") == "true";
+}
+
+function isSpecialItem(aElt)
+{
+ return aElt.localName == "toolbarseparator" ||
+ aElt.localName == "toolbarspring" ||
+ aElt.localName == "toolbarspacer";
+}
+
+function isToolbarItem(aElt)
+{
+ return aElt.localName == "toolbarbutton" ||
+ aElt.localName == "toolbaritem" ||
+ aElt.localName == "toolbarseparator" ||
+ aElt.localName == "toolbarspring" ||
+ aElt.localName == "toolbarspacer";
+}
+
+// Drag and Drop observers
+
+function onToolbarDragExit(aEvent)
+{
+ if (isUnwantedDragEvent(aEvent)) {
+ return;
+ }
+
+ if (gCurrentDragOverItem)
+ setDragActive(gCurrentDragOverItem, false);
+}
+
+function onToolbarDragStart(aEvent)
+{
+ var item = aEvent.target;
+ while (item && item.localName != "toolbarpaletteitem") {
+ if (item.localName == "toolbar")
+ return;
+ item = item.parentNode;
+ }
+
+ item.setAttribute("dragactive", "true");
+
+ var dt = aEvent.dataTransfer;
+ var documentId = gToolboxDocument.documentElement.id;
+ dt.setData("text/toolbarwrapper-id/" + documentId, item.firstChild.id);
+ dt.effectAllowed = "move";
+}
+
+function onToolbarDragOver(aEvent)
+{
+ if (isUnwantedDragEvent(aEvent)) {
+ return;
+ }
+
+ var documentId = gToolboxDocument.documentElement.id;
+ if (!aEvent.dataTransfer.types.includes("text/toolbarwrapper-id/" + documentId.toLowerCase()))
+ return;
+
+ var toolbar = aEvent.target;
+ var dropTarget = aEvent.target;
+ while (toolbar && toolbar.localName != "toolbar") {
+ dropTarget = toolbar;
+ toolbar = toolbar.parentNode;
+ }
+
+ // Make sure we are dragging over a customizable toolbar.
+ if (!toolbar || !isCustomizableToolbar(toolbar)) {
+ gCurrentDragOverItem = null;
+ return;
+ }
+
+ var previousDragItem = gCurrentDragOverItem;
+
+ if (dropTarget.localName == "toolbar") {
+ gCurrentDragOverItem = dropTarget;
+ } else {
+ gCurrentDragOverItem = null;
+
+ var direction = window.getComputedStyle(dropTarget.parentNode, null).direction;
+ var dropTargetCenter = dropTarget.boxObject.x + (dropTarget.boxObject.width / 2);
+ var dragAfter;
+ if (direction == "ltr")
+ dragAfter = aEvent.clientX > dropTargetCenter;
+ else
+ dragAfter = aEvent.clientX < dropTargetCenter;
+
+ if (dragAfter) {
+ gCurrentDragOverItem = dropTarget.nextSibling;
+ if (!gCurrentDragOverItem)
+ gCurrentDragOverItem = toolbar;
+ } else
+ gCurrentDragOverItem = dropTarget;
+ }
+
+ if (previousDragItem && gCurrentDragOverItem != previousDragItem) {
+ setDragActive(previousDragItem, false);
+ }
+
+ setDragActive(gCurrentDragOverItem, true);
+
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+}
+
+function onToolbarDrop(aEvent)
+{
+ if (isUnwantedDragEvent(aEvent)) {
+ return;
+ }
+
+ if (!gCurrentDragOverItem)
+ return;
+
+ setDragActive(gCurrentDragOverItem, false);
+
+ var documentId = gToolboxDocument.documentElement.id;
+ var draggedItemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId);
+ if (gCurrentDragOverItem.id == draggedItemId)
+ return;
+
+ var toolbar = aEvent.target;
+ while (toolbar.localName != "toolbar")
+ toolbar = toolbar.parentNode;
+
+ var draggedPaletteWrapper = document.getElementById("wrapper-"+draggedItemId);
+ if (!draggedPaletteWrapper) {
+ // The wrapper has been dragged from the toolbar.
+ // Get the wrapper from the toolbar document and make sure that
+ // it isn't being dropped on itself.
+ let wrapper = gToolboxDocument.getElementById("wrapper-"+draggedItemId);
+ if (wrapper == gCurrentDragOverItem)
+ return;
+
+ // Don't allow non-removable kids (e.g., the menubar) to move.
+ if (wrapper.firstChild.getAttribute("removable") != "true")
+ return;
+
+ // Remove the item from its place in the toolbar.
+ wrapper.parentNode.removeChild(wrapper);
+
+ // Determine which toolbar we are dropping on.
+ var dropToolbar = null;
+ if (gCurrentDragOverItem.localName == "toolbar")
+ dropToolbar = gCurrentDragOverItem;
+ else
+ dropToolbar = gCurrentDragOverItem.parentNode;
+
+ // Insert the item into the toolbar.
+ if (gCurrentDragOverItem != dropToolbar)
+ dropToolbar.insertBefore(wrapper, gCurrentDragOverItem);
+ else
+ dropToolbar.appendChild(wrapper);
+ } else {
+ // The item has been dragged from the palette
+
+ // Create a new wrapper for the item. We don't know the id yet.
+ let wrapper = createWrapper("", gToolboxDocument);
+
+ // Ask the toolbar to clone the item's template, place it inside the wrapper, and insert it in the toolbar.
+ var newItem = toolbar.insertItem(draggedItemId, gCurrentDragOverItem == toolbar ? null : gCurrentDragOverItem, wrapper);
+
+ // Prepare the item and wrapper to look good on the toolbar.
+ cleanupItemForToolbar(newItem, wrapper);
+ wrapper.id = "wrapper-"+newItem.id;
+ wrapper.flex = newItem.flex;
+
+ // Remove the wrapper from the palette.
+ if (draggedItemId != "separator" &&
+ draggedItemId != "spring" &&
+ draggedItemId != "spacer")
+ gPaletteBox.removeChild(draggedPaletteWrapper);
+ }
+
+ gCurrentDragOverItem = null;
+
+ toolboxChanged();
+}
+
+function onPaletteDragOver(aEvent)
+{
+ if (isUnwantedDragEvent(aEvent)) {
+ return;
+ }
+ var documentId = gToolboxDocument.documentElement.id;
+ if (aEvent.dataTransfer.types.includes("text/toolbarwrapper-id/" + documentId.toLowerCase()))
+ aEvent.preventDefault();
+}
+
+function onPaletteDrop(aEvent)
+{
+ if (isUnwantedDragEvent(aEvent)) {
+ return;
+ }
+ var documentId = gToolboxDocument.documentElement.id;
+ var itemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId);
+
+ var wrapper = gToolboxDocument.getElementById("wrapper-"+itemId);
+ if (wrapper) {
+ // Don't allow non-removable kids (e.g., the menubar) to move.
+ if (wrapper.firstChild.getAttribute("removable") != "true")
+ return;
+
+ var wrapperType = wrapper.getAttribute("type");
+ if (wrapperType != "separator" &&
+ wrapperType != "spacer" &&
+ wrapperType != "spring") {
+ restoreItemForToolbar(wrapper.firstChild, wrapper);
+ wrapPaletteItem(document.importNode(wrapper.firstChild, true));
+ gToolbox.palette.appendChild(wrapper.firstChild);
+ }
+
+ // The item was dragged out of the toolbar.
+ wrapper.parentNode.removeChild(wrapper);
+ }
+
+ toolboxChanged();
+}
+
+
+function isUnwantedDragEvent(aEvent) {
+ try {
+ if (Services.prefs.getBoolPref("toolkit.customization.unsafe_drag_events")) {
+ return false;
+ }
+ } catch (ex) {}
+
+ /* Discard drag events that originated from a separate window to
+ prevent content->chrome privilege escalations. */
+ let mozSourceNode = aEvent.dataTransfer.mozSourceNode;
+ // mozSourceNode is null in the dragStart event handler or if
+ // the drag event originated in an external application.
+ if (!mozSourceNode) {
+ return true;
+ }
+ let sourceWindow = mozSourceNode.ownerDocument.defaultView;
+ return sourceWindow != window && sourceWindow != gToolboxDocument.defaultView;
+}
+
diff --git a/navigator/components/toolbar/content/customizeToolbar.xul b/navigator/components/toolbar/content/customizeToolbar.xul
new file mode 100644
index 00000000..14d8d4b2
--- /dev/null
+++ b/navigator/components/toolbar/content/customizeToolbar.xul
@@ -0,0 +1,64 @@
+
+
+
+
+
+ %brandDTD;
+
+ %customizeToolbarDTD;
+]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/navigator/components/toolbar/jar.mn b/navigator/components/toolbar/jar.mn
new file mode 100644
index 00000000..74aa4a00
--- /dev/null
+++ b/navigator/components/toolbar/jar.mn
@@ -0,0 +1,13 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+navigator.jar:
+% override chrome://global/content/customizeToolbar.xul chrome://navigator/content/toolbar/customizeToolbar.xul
+ content/navigator/toolbar/customizeToolbar.css (content/customizeToolbar.css)
+* content/navigator/toolbar/customizeToolbar.js (content/customizeToolbar.js)
+ content/navigator/toolbar/customizeToolbar.xul (content/customizeToolbar.xul)
+
+en-US.jar:
+ locale/en-US/navigator/toolbar/customizeToolbar.dtd (locale/customizeToolbar.dtd)
+ locale/en-US/navigator/toolbar/customizeToolbar.properties (locale/customizeToolbar.properties)
diff --git a/navigator/components/toolbar/locale/customizeToolbar.dtd b/navigator/components/toolbar/locale/customizeToolbar.dtd
new file mode 100644
index 00000000..4ca21b57
--- /dev/null
+++ b/navigator/components/toolbar/locale/customizeToolbar.dtd
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/navigator/components/toolbar/locale/customizeToolbar.properties b/navigator/components/toolbar/locale/customizeToolbar.properties
new file mode 100644
index 00000000..b19152fa
--- /dev/null
+++ b/navigator/components/toolbar/locale/customizeToolbar.properties
@@ -0,0 +1,12 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+enterToolbarTitle=New Toolbar
+enterToolbarName=Enter a name for this toolbar:
+enterToolbarDup=There is already a toolbar with the name ā%Sā. Please enter a different name.
+enterToolbarIllegalChars=The name contains illegal character "|". Please enter a different name.
+enterToolbarBlank=You must enter a name to create a new toolbar.
+separatorTitle=Separator
+springTitle=Flexible Space
+spacerTitle=Space
diff --git a/navigator/components/toolbar/moz.build b/navigator/components/toolbar/moz.build
new file mode 100644
index 00000000..697e0cda
--- /dev/null
+++ b/navigator/components/toolbar/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file