michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: var gToolboxDocument = null; michael@0: var gToolbox = null; michael@0: var gCurrentDragOverItem = null; michael@0: var gToolboxChanged = false; michael@0: var gToolboxSheet = false; michael@0: var gPaletteBox = null; michael@0: michael@0: Components.utils.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: function onLoad() michael@0: { michael@0: if ("arguments" in window && window.arguments[0]) { michael@0: InitWithToolbox(window.arguments[0]); michael@0: repositionDialog(window); michael@0: } michael@0: else if (window.frameElement && michael@0: "toolbox" in window.frameElement) { michael@0: gToolboxSheet = true; michael@0: InitWithToolbox(window.frameElement.toolbox); michael@0: repositionDialog(window.frameElement.panel); michael@0: } michael@0: } michael@0: michael@0: function InitWithToolbox(aToolbox) michael@0: { michael@0: gToolbox = aToolbox; michael@0: dispatchCustomizationEvent("beforecustomization"); michael@0: gToolboxDocument = gToolbox.ownerDocument; michael@0: gToolbox.customizing = true; michael@0: forEachCustomizableToolbar(function (toolbar) { michael@0: toolbar.setAttribute("customizing", "true"); michael@0: }); michael@0: gPaletteBox = document.getElementById("palette-box"); michael@0: michael@0: var elts = getRootElements(); michael@0: for (let i=0; i < elts.length; i++) { michael@0: elts[i].addEventListener("dragstart", onToolbarDragStart, true); michael@0: elts[i].addEventListener("dragover", onToolbarDragOver, true); michael@0: elts[i].addEventListener("dragexit", onToolbarDragExit, true); michael@0: elts[i].addEventListener("drop", onToolbarDrop, true); michael@0: } michael@0: michael@0: initDialog(); michael@0: } michael@0: michael@0: function onClose() michael@0: { michael@0: if (!gToolboxSheet) michael@0: window.close(); michael@0: else michael@0: finishToolbarCustomization(); michael@0: } michael@0: michael@0: function onUnload() michael@0: { michael@0: if (!gToolboxSheet) michael@0: finishToolbarCustomization(); michael@0: } michael@0: michael@0: function finishToolbarCustomization() michael@0: { michael@0: removeToolboxListeners(); michael@0: unwrapToolbarItems(); michael@0: persistCurrentSets(); michael@0: gToolbox.customizing = false; michael@0: forEachCustomizableToolbar(function (toolbar) { michael@0: toolbar.removeAttribute("customizing"); michael@0: }); michael@0: michael@0: notifyParentComplete(); michael@0: } michael@0: michael@0: function initDialog() michael@0: { michael@0: if (!gToolbox.toolbarset) { michael@0: document.getElementById("newtoolbar").hidden = true; michael@0: } michael@0: michael@0: var mode = gToolbox.getAttribute("mode"); michael@0: document.getElementById("modelist").value = mode; michael@0: var smallIconsCheckbox = document.getElementById("smallicons"); michael@0: smallIconsCheckbox.checked = gToolbox.getAttribute("iconsize") == "small"; michael@0: if (mode == "text") michael@0: smallIconsCheckbox.disabled = true; michael@0: michael@0: // Build up the palette of other items. michael@0: buildPalette(); michael@0: michael@0: // Wrap all the items on the toolbar in toolbarpaletteitems. michael@0: wrapToolbarItems(); michael@0: } michael@0: michael@0: function repositionDialog(aWindow) michael@0: { michael@0: // Position the dialog touching the bottom of the toolbox and centered with michael@0: // it. michael@0: if (!aWindow) michael@0: return; michael@0: michael@0: var width; michael@0: if (aWindow != window) michael@0: width = aWindow.getBoundingClientRect().width; michael@0: else if (document.documentElement.hasAttribute("width")) michael@0: width = document.documentElement.getAttribute("width"); michael@0: else michael@0: width = parseInt(document.documentElement.style.width); michael@0: var screenX = gToolbox.boxObject.screenX michael@0: + ((gToolbox.boxObject.width - width) / 2); michael@0: var screenY = gToolbox.boxObject.screenY + gToolbox.boxObject.height; michael@0: michael@0: aWindow.moveTo(screenX, screenY); michael@0: } michael@0: michael@0: function removeToolboxListeners() michael@0: { michael@0: var elts = getRootElements(); michael@0: for (let i=0; i < elts.length; i++) { michael@0: elts[i].removeEventListener("dragstart", onToolbarDragStart, true); michael@0: elts[i].removeEventListener("dragover", onToolbarDragOver, true); michael@0: elts[i].removeEventListener("dragexit", onToolbarDragExit, true); michael@0: elts[i].removeEventListener("drop", onToolbarDrop, true); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Invoke a callback on the toolbox to notify it that the dialog is done michael@0: * and going away. michael@0: */ michael@0: function notifyParentComplete() michael@0: { michael@0: if ("customizeDone" in gToolbox) michael@0: gToolbox.customizeDone(gToolboxChanged); michael@0: dispatchCustomizationEvent("aftercustomization"); michael@0: } michael@0: michael@0: function toolboxChanged(aType) michael@0: { michael@0: gToolboxChanged = true; michael@0: if ("customizeChange" in gToolbox) michael@0: gToolbox.customizeChange(aType); michael@0: dispatchCustomizationEvent("customizationchange"); michael@0: } michael@0: michael@0: function dispatchCustomizationEvent(aEventName) { michael@0: var evt = document.createEvent("Events"); michael@0: evt.initEvent(aEventName, true, true); michael@0: gToolbox.dispatchEvent(evt); michael@0: } michael@0: michael@0: /** michael@0: * Persist the current set of buttons in all customizable toolbars to michael@0: * localstore. michael@0: */ michael@0: function persistCurrentSets() michael@0: { michael@0: if (!gToolboxChanged || gToolboxDocument.defaultView.closed) michael@0: return; michael@0: michael@0: var customCount = 0; michael@0: forEachCustomizableToolbar(function (toolbar) { michael@0: // Calculate currentset and store it in the attribute. michael@0: var currentSet = toolbar.currentSet; michael@0: toolbar.setAttribute("currentset", currentSet); michael@0: michael@0: var customIndex = toolbar.hasAttribute("customindex"); michael@0: if (customIndex) { michael@0: if (!toolbar.hasChildNodes()) { michael@0: // Remove custom toolbars whose contents have been removed. michael@0: gToolbox.removeChild(toolbar); michael@0: } else if (gToolbox.toolbarset) { michael@0: // Persist custom toolbar info on the michael@0: gToolbox.toolbarset.setAttribute("toolbar"+(++customCount), michael@0: toolbar.toolbarName + ":" + currentSet); michael@0: gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount); michael@0: } michael@0: } michael@0: michael@0: if (!customIndex) { michael@0: // Persist the currentset attribute directly on hardcoded toolbars. michael@0: gToolboxDocument.persist(toolbar.id, "currentset"); michael@0: } michael@0: }); michael@0: michael@0: // Remove toolbarX attributes for removed toolbars. michael@0: while (gToolbox.toolbarset && gToolbox.toolbarset.hasAttribute("toolbar"+(++customCount))) { michael@0: gToolbox.toolbarset.removeAttribute("toolbar"+customCount); michael@0: gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Wraps all items in all customizable toolbars in a toolbox. michael@0: */ michael@0: function wrapToolbarItems() michael@0: { michael@0: forEachCustomizableToolbar(function (toolbar) { michael@0: Array.forEach(toolbar.childNodes, function (item) { michael@0: #ifdef XP_MACOSX michael@0: if (item.firstChild && item.firstChild.localName == "menubar") michael@0: return; michael@0: #endif michael@0: if (isToolbarItem(item)) { michael@0: let wrapper = wrapToolbarItem(item); michael@0: cleanupItemForToolbar(item, wrapper); michael@0: } michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: function getRootElements() michael@0: { michael@0: return [gToolbox].concat(gToolbox.externalToolbars); michael@0: } michael@0: michael@0: /** michael@0: * Unwraps all items in all customizable toolbars in a toolbox. michael@0: */ michael@0: function unwrapToolbarItems() michael@0: { michael@0: let elts = getRootElements(); michael@0: for (let i=0; i < elts.length; i++) { michael@0: let paletteItems = elts[i].getElementsByTagName("toolbarpaletteitem"); michael@0: let paletteItem; michael@0: while ((paletteItem = paletteItems.item(0)) != null) { michael@0: let toolbarItem = paletteItem.firstChild; michael@0: restoreItemForToolbar(toolbarItem, paletteItem); michael@0: paletteItem.parentNode.replaceChild(toolbarItem, paletteItem); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Creates a wrapper that can be used to contain a toolbaritem and prevent michael@0: * it from receiving UI events. michael@0: */ michael@0: function createWrapper(aId, aDocument) michael@0: { michael@0: var wrapper = aDocument.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", michael@0: "toolbarpaletteitem"); michael@0: michael@0: wrapper.id = "wrapper-"+aId; michael@0: return wrapper; michael@0: } michael@0: michael@0: /** michael@0: * Wraps an item that has been cloned from a template and adds michael@0: * it to the end of the palette. michael@0: */ michael@0: function wrapPaletteItem(aPaletteItem) michael@0: { michael@0: var wrapper = createWrapper(aPaletteItem.id, document); michael@0: michael@0: wrapper.appendChild(aPaletteItem); michael@0: michael@0: // XXX We need to call this AFTER the palette item has been appended michael@0: // to the wrapper or else we crash dropping certain buttons on the michael@0: // palette due to removal of the command and disabled attributes - JRH michael@0: cleanUpItemForPalette(aPaletteItem, wrapper); michael@0: michael@0: gPaletteBox.appendChild(wrapper); michael@0: } michael@0: michael@0: /** michael@0: * Wraps an item that is currently on a toolbar and replaces the item michael@0: * with the wrapper. This is not used when dropping items from the palette, michael@0: * only when first starting the dialog and wrapping everything on the toolbars. michael@0: */ michael@0: function wrapToolbarItem(aToolbarItem) michael@0: { michael@0: var wrapper = createWrapper(aToolbarItem.id, gToolboxDocument); michael@0: michael@0: wrapper.flex = aToolbarItem.flex; michael@0: michael@0: aToolbarItem.parentNode.replaceChild(wrapper, aToolbarItem); michael@0: michael@0: wrapper.appendChild(aToolbarItem); michael@0: michael@0: return wrapper; michael@0: } michael@0: michael@0: /** michael@0: * Get the list of ids for the current set of items on each toolbar. michael@0: */ michael@0: function getCurrentItemIds() michael@0: { michael@0: var currentItems = {}; michael@0: forEachCustomizableToolbar(function (toolbar) { michael@0: var child = toolbar.firstChild; michael@0: while (child) { michael@0: if (isToolbarItem(child)) michael@0: currentItems[child.id] = 1; michael@0: child = child.nextSibling; michael@0: } michael@0: }); michael@0: return currentItems; michael@0: } michael@0: michael@0: /** michael@0: * Builds the palette of draggable items that are not yet in a toolbar. michael@0: */ michael@0: function buildPalette() michael@0: { michael@0: // Empty the palette first. michael@0: while (gPaletteBox.lastChild) michael@0: gPaletteBox.removeChild(gPaletteBox.lastChild); michael@0: michael@0: // Add the toolbar separator item. michael@0: var templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", michael@0: "toolbarseparator"); michael@0: templateNode.id = "separator"; michael@0: wrapPaletteItem(templateNode); michael@0: michael@0: // Add the toolbar spring item. michael@0: templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", michael@0: "toolbarspring"); michael@0: templateNode.id = "spring"; michael@0: templateNode.flex = 1; michael@0: wrapPaletteItem(templateNode); michael@0: michael@0: // Add the toolbar spacer item. michael@0: templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", michael@0: "toolbarspacer"); michael@0: templateNode.id = "spacer"; michael@0: templateNode.flex = 1; michael@0: wrapPaletteItem(templateNode); michael@0: michael@0: var currentItems = getCurrentItemIds(); michael@0: templateNode = gToolbox.palette.firstChild; michael@0: while (templateNode) { michael@0: // Check if the item is already in a toolbar before adding it to the palette. michael@0: if (!(templateNode.id in currentItems)) { michael@0: var paletteItem = document.importNode(templateNode, true); michael@0: wrapPaletteItem(paletteItem); michael@0: } michael@0: michael@0: templateNode = templateNode.nextSibling; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Makes sure that an item that has been cloned from a template michael@0: * is stripped of any attributes that may adversely affect its michael@0: * appearance in the palette. michael@0: */ michael@0: function cleanUpItemForPalette(aItem, aWrapper) michael@0: { michael@0: aWrapper.setAttribute("place", "palette"); michael@0: setWrapperType(aItem, aWrapper); michael@0: michael@0: if (aItem.hasAttribute("title")) michael@0: aWrapper.setAttribute("title", aItem.getAttribute("title")); michael@0: else if (aItem.hasAttribute("label")) michael@0: aWrapper.setAttribute("title", aItem.getAttribute("label")); michael@0: else if (isSpecialItem(aItem)) { michael@0: var stringBundle = document.getElementById("stringBundle"); michael@0: // Remove the common "toolbar" prefix to generate the string name. michael@0: var title = stringBundle.getString(aItem.localName.slice(7) + "Title"); michael@0: aWrapper.setAttribute("title", title); michael@0: } michael@0: aWrapper.setAttribute("tooltiptext", aWrapper.getAttribute("title")); michael@0: michael@0: // Remove attributes that screw up our appearance. michael@0: aItem.removeAttribute("command"); michael@0: aItem.removeAttribute("observes"); michael@0: aItem.removeAttribute("type"); michael@0: aItem.removeAttribute("width"); michael@0: michael@0: Array.forEach(aWrapper.querySelectorAll("[disabled]"), function(aNode) { michael@0: aNode.removeAttribute("disabled"); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Makes sure that an item that has been cloned from a template michael@0: * is stripped of all properties that may adversely affect its michael@0: * appearance in the toolbar. Store critical properties on the michael@0: * wrapper so they can be put back on the item when we're done. michael@0: */ michael@0: function cleanupItemForToolbar(aItem, aWrapper) michael@0: { michael@0: setWrapperType(aItem, aWrapper); michael@0: aWrapper.setAttribute("place", "toolbar"); michael@0: michael@0: if (aItem.hasAttribute("command")) { michael@0: aWrapper.setAttribute("itemcommand", aItem.getAttribute("command")); michael@0: aItem.removeAttribute("command"); michael@0: } michael@0: michael@0: if (aItem.checked) { michael@0: aWrapper.setAttribute("itemchecked", "true"); michael@0: aItem.checked = false; michael@0: } michael@0: michael@0: if (aItem.disabled) { michael@0: aWrapper.setAttribute("itemdisabled", "true"); michael@0: aItem.disabled = false; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Restore all the properties that we stripped off above. michael@0: */ michael@0: function restoreItemForToolbar(aItem, aWrapper) michael@0: { michael@0: if (aWrapper.hasAttribute("itemdisabled")) michael@0: aItem.disabled = true; michael@0: michael@0: if (aWrapper.hasAttribute("itemchecked")) michael@0: aItem.checked = true; michael@0: michael@0: if (aWrapper.hasAttribute("itemcommand")) { michael@0: let commandID = aWrapper.getAttribute("itemcommand"); michael@0: aItem.setAttribute("command", commandID); michael@0: michael@0: //XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing michael@0: let command = gToolboxDocument.getElementById(commandID); michael@0: if (command && command.hasAttribute("disabled")) michael@0: aItem.setAttribute("disabled", command.getAttribute("disabled")); michael@0: } michael@0: } michael@0: michael@0: function setWrapperType(aItem, aWrapper) michael@0: { michael@0: if (aItem.localName == "toolbarseparator") { michael@0: aWrapper.setAttribute("type", "separator"); michael@0: } else if (aItem.localName == "toolbarspring") { michael@0: aWrapper.setAttribute("type", "spring"); michael@0: } else if (aItem.localName == "toolbarspacer") { michael@0: aWrapper.setAttribute("type", "spacer"); michael@0: } else if (aItem.localName == "toolbaritem" && aItem.firstChild) { michael@0: aWrapper.setAttribute("type", aItem.firstChild.localName); michael@0: } michael@0: } michael@0: michael@0: function setDragActive(aItem, aValue) michael@0: { michael@0: var node = aItem; michael@0: var direction = window.getComputedStyle(aItem, null).direction; michael@0: var value = direction == "ltr"? "left" : "right"; michael@0: if (aItem.localName == "toolbar") { michael@0: node = aItem.lastChild; michael@0: value = direction == "ltr"? "right" : "left"; michael@0: } michael@0: michael@0: if (!node) michael@0: return; michael@0: michael@0: if (aValue) { michael@0: if (!node.hasAttribute("dragover")) michael@0: node.setAttribute("dragover", value); michael@0: } else { michael@0: node.removeAttribute("dragover"); michael@0: } michael@0: } michael@0: michael@0: function addNewToolbar() michael@0: { michael@0: var promptService = Services.prompt; michael@0: var stringBundle = document.getElementById("stringBundle"); michael@0: var message = stringBundle.getString("enterToolbarName"); michael@0: var title = stringBundle.getString("enterToolbarTitle"); michael@0: michael@0: var name = {}; michael@0: michael@0: // Quitting from the toolbar dialog while the new toolbar prompt is up michael@0: // can cause things to become unresponsive on the Mac. Until dialog modality michael@0: // is fixed (395465), disable the "Done" button explicitly. michael@0: var doneButton = document.getElementById("donebutton"); michael@0: doneButton.disabled = true; michael@0: michael@0: while (true) { michael@0: michael@0: if (!promptService.prompt(window, title, message, name, null, {})) { michael@0: doneButton.disabled = false; michael@0: return; michael@0: } michael@0: michael@0: if (!name.value) { michael@0: message = stringBundle.getFormattedString("enterToolbarBlank", [name.value]); michael@0: continue; michael@0: } michael@0: michael@0: var dupeFound = false; michael@0: michael@0: // Check for an existing toolbar with the same display name michael@0: for (let i = 0; i < gToolbox.childNodes.length; ++i) { michael@0: var toolbar = gToolbox.childNodes[i]; michael@0: var toolbarName = toolbar.getAttribute("toolbarname"); michael@0: michael@0: if (toolbarName == name.value && michael@0: toolbar.getAttribute("type") != "menubar" && michael@0: toolbar.nodeName == 'toolbar') { michael@0: dupeFound = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!dupeFound) michael@0: break; michael@0: michael@0: message = stringBundle.getFormattedString("enterToolbarDup", [name.value]); michael@0: } michael@0: michael@0: gToolbox.appendCustomToolbar(name.value, ""); michael@0: michael@0: toolboxChanged(); michael@0: michael@0: doneButton.disabled = false; michael@0: } michael@0: michael@0: /** michael@0: * Restore the default set of buttons to fixed toolbars, michael@0: * remove all custom toolbars, and rebuild the palette. michael@0: */ michael@0: function restoreDefaultSet() michael@0: { michael@0: // Unwrap the items on the toolbar. michael@0: unwrapToolbarItems(); michael@0: michael@0: // Remove all of the customized toolbars. michael@0: var child = gToolbox.lastChild; michael@0: while (child) { michael@0: if (child.hasAttribute("customindex")) { michael@0: var thisChild = child; michael@0: child = child.previousSibling; michael@0: thisChild.currentSet = "__empty"; michael@0: gToolbox.removeChild(thisChild); michael@0: } else { michael@0: child = child.previousSibling; michael@0: } michael@0: } michael@0: michael@0: // Restore the defaultset for fixed toolbars. michael@0: forEachCustomizableToolbar(function (toolbar) { michael@0: var defaultSet = toolbar.getAttribute("defaultset"); michael@0: if (defaultSet) michael@0: toolbar.currentSet = defaultSet; michael@0: }); michael@0: michael@0: // Restore the default icon size and mode. michael@0: document.getElementById("smallicons").checked = (updateIconSize() == "small"); michael@0: document.getElementById("modelist").value = updateToolbarMode(); michael@0: michael@0: // Now rebuild the palette. michael@0: buildPalette(); michael@0: michael@0: // Now re-wrap the items on the toolbar. michael@0: wrapToolbarItems(); michael@0: michael@0: toolboxChanged("reset"); michael@0: } michael@0: michael@0: function updateIconSize(aSize) { michael@0: return updateToolboxProperty("iconsize", aSize, "large"); michael@0: } michael@0: michael@0: function updateToolbarMode(aModeValue) { michael@0: var mode = updateToolboxProperty("mode", aModeValue, "icons"); michael@0: michael@0: var iconSizeCheckbox = document.getElementById("smallicons"); michael@0: iconSizeCheckbox.disabled = mode == "text"; michael@0: michael@0: return mode; michael@0: } michael@0: michael@0: function updateToolboxProperty(aProp, aValue, aToolkitDefault) { michael@0: var toolboxDefault = gToolbox.getAttribute("default" + aProp) || michael@0: aToolkitDefault; michael@0: michael@0: gToolbox.setAttribute(aProp, aValue || toolboxDefault); michael@0: gToolboxDocument.persist(gToolbox.id, aProp); michael@0: michael@0: forEachCustomizableToolbar(function (toolbar) { michael@0: var toolbarDefault = toolbar.getAttribute("default" + aProp) || michael@0: toolboxDefault; michael@0: if (toolbar.getAttribute("lock" + aProp) == "true" && michael@0: toolbar.getAttribute(aProp) == toolbarDefault) michael@0: return; michael@0: michael@0: toolbar.setAttribute(aProp, aValue || toolbarDefault); michael@0: gToolboxDocument.persist(toolbar.id, aProp); michael@0: }); michael@0: michael@0: toolboxChanged(aProp); michael@0: michael@0: return aValue || toolboxDefault; michael@0: } michael@0: michael@0: function forEachCustomizableToolbar(callback) { michael@0: Array.filter(gToolbox.childNodes, isCustomizableToolbar).forEach(callback); michael@0: Array.filter(gToolbox.externalToolbars, isCustomizableToolbar).forEach(callback); michael@0: } michael@0: michael@0: function isCustomizableToolbar(aElt) michael@0: { michael@0: return aElt.localName == "toolbar" && michael@0: aElt.getAttribute("customizable") == "true"; michael@0: } michael@0: michael@0: function isSpecialItem(aElt) michael@0: { michael@0: return aElt.localName == "toolbarseparator" || michael@0: aElt.localName == "toolbarspring" || michael@0: aElt.localName == "toolbarspacer"; michael@0: } michael@0: michael@0: function isToolbarItem(aElt) michael@0: { michael@0: return aElt.localName == "toolbarbutton" || michael@0: aElt.localName == "toolbaritem" || michael@0: aElt.localName == "toolbarseparator" || michael@0: aElt.localName == "toolbarspring" || michael@0: aElt.localName == "toolbarspacer"; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: //// Drag and Drop observers michael@0: michael@0: function onToolbarDragExit(aEvent) michael@0: { michael@0: if (isUnwantedDragEvent(aEvent)) { michael@0: return; michael@0: } michael@0: michael@0: if (gCurrentDragOverItem) michael@0: setDragActive(gCurrentDragOverItem, false); michael@0: } michael@0: michael@0: function onToolbarDragStart(aEvent) michael@0: { michael@0: var item = aEvent.target; michael@0: while (item && item.localName != "toolbarpaletteitem") { michael@0: if (item.localName == "toolbar") michael@0: return; michael@0: item = item.parentNode; michael@0: } michael@0: michael@0: item.setAttribute("dragactive", "true"); michael@0: michael@0: var dt = aEvent.dataTransfer; michael@0: var documentId = gToolboxDocument.documentElement.id; michael@0: dt.setData("text/toolbarwrapper-id/" + documentId, item.firstChild.id); michael@0: dt.effectAllowed = "move"; michael@0: } michael@0: michael@0: function onToolbarDragOver(aEvent) michael@0: { michael@0: if (isUnwantedDragEvent(aEvent)) { michael@0: return; michael@0: } michael@0: michael@0: var documentId = gToolboxDocument.documentElement.id; michael@0: if (!aEvent.dataTransfer.types.contains("text/toolbarwrapper-id/" + documentId.toLowerCase())) michael@0: return; michael@0: michael@0: var toolbar = aEvent.target; michael@0: var dropTarget = aEvent.target; michael@0: while (toolbar && toolbar.localName != "toolbar") { michael@0: dropTarget = toolbar; michael@0: toolbar = toolbar.parentNode; michael@0: } michael@0: michael@0: // Make sure we are dragging over a customizable toolbar. michael@0: if (!toolbar || !isCustomizableToolbar(toolbar)) { michael@0: gCurrentDragOverItem = null; michael@0: return; michael@0: } michael@0: michael@0: var previousDragItem = gCurrentDragOverItem; michael@0: michael@0: if (dropTarget.localName == "toolbar") { michael@0: gCurrentDragOverItem = dropTarget; michael@0: } else { michael@0: gCurrentDragOverItem = null; michael@0: michael@0: var direction = window.getComputedStyle(dropTarget.parentNode, null).direction; michael@0: var dropTargetCenter = dropTarget.boxObject.x + (dropTarget.boxObject.width / 2); michael@0: var dragAfter; michael@0: if (direction == "ltr") michael@0: dragAfter = aEvent.clientX > dropTargetCenter; michael@0: else michael@0: dragAfter = aEvent.clientX < dropTargetCenter; michael@0: michael@0: if (dragAfter) { michael@0: gCurrentDragOverItem = dropTarget.nextSibling; michael@0: if (!gCurrentDragOverItem) michael@0: gCurrentDragOverItem = toolbar; michael@0: } else michael@0: gCurrentDragOverItem = dropTarget; michael@0: } michael@0: michael@0: if (previousDragItem && gCurrentDragOverItem != previousDragItem) { michael@0: setDragActive(previousDragItem, false); michael@0: } michael@0: michael@0: setDragActive(gCurrentDragOverItem, true); michael@0: michael@0: aEvent.preventDefault(); michael@0: aEvent.stopPropagation(); michael@0: } michael@0: michael@0: function onToolbarDrop(aEvent) michael@0: { michael@0: if (isUnwantedDragEvent(aEvent)) { michael@0: return; michael@0: } michael@0: michael@0: if (!gCurrentDragOverItem) michael@0: return; michael@0: michael@0: setDragActive(gCurrentDragOverItem, false); michael@0: michael@0: var documentId = gToolboxDocument.documentElement.id; michael@0: var draggedItemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId); michael@0: if (gCurrentDragOverItem.id == draggedItemId) michael@0: return; michael@0: michael@0: var toolbar = aEvent.target; michael@0: while (toolbar.localName != "toolbar") michael@0: toolbar = toolbar.parentNode; michael@0: michael@0: var draggedPaletteWrapper = document.getElementById("wrapper-"+draggedItemId); michael@0: if (!draggedPaletteWrapper) { michael@0: // The wrapper has been dragged from the toolbar. michael@0: // Get the wrapper from the toolbar document and make sure that michael@0: // it isn't being dropped on itself. michael@0: var wrapper = gToolboxDocument.getElementById("wrapper-"+draggedItemId); michael@0: if (wrapper == gCurrentDragOverItem) michael@0: return; michael@0: michael@0: // Don't allow non-removable kids (e.g., the menubar) to move. michael@0: if (wrapper.firstChild.getAttribute("removable") != "true") michael@0: return; michael@0: michael@0: // Remove the item from its place in the toolbar. michael@0: wrapper.parentNode.removeChild(wrapper); michael@0: michael@0: // Determine which toolbar we are dropping on. michael@0: var dropToolbar = null; michael@0: if (gCurrentDragOverItem.localName == "toolbar") michael@0: dropToolbar = gCurrentDragOverItem; michael@0: else michael@0: dropToolbar = gCurrentDragOverItem.parentNode; michael@0: michael@0: // Insert the item into the toolbar. michael@0: if (gCurrentDragOverItem != dropToolbar) michael@0: dropToolbar.insertBefore(wrapper, gCurrentDragOverItem); michael@0: else michael@0: dropToolbar.appendChild(wrapper); michael@0: } else { michael@0: // The item has been dragged from the palette michael@0: michael@0: // Create a new wrapper for the item. We don't know the id yet. michael@0: var wrapper = createWrapper("", gToolboxDocument); michael@0: michael@0: // Ask the toolbar to clone the item's template, place it inside the wrapper, and insert it in the toolbar. michael@0: var newItem = toolbar.insertItem(draggedItemId, gCurrentDragOverItem == toolbar ? null : gCurrentDragOverItem, wrapper); michael@0: michael@0: // Prepare the item and wrapper to look good on the toolbar. michael@0: cleanupItemForToolbar(newItem, wrapper); michael@0: wrapper.id = "wrapper-"+newItem.id; michael@0: wrapper.flex = newItem.flex; michael@0: michael@0: // Remove the wrapper from the palette. michael@0: if (draggedItemId != "separator" && michael@0: draggedItemId != "spring" && michael@0: draggedItemId != "spacer") michael@0: gPaletteBox.removeChild(draggedPaletteWrapper); michael@0: } michael@0: michael@0: gCurrentDragOverItem = null; michael@0: michael@0: toolboxChanged(); michael@0: }; michael@0: michael@0: function onPaletteDragOver(aEvent) michael@0: { michael@0: if (isUnwantedDragEvent(aEvent)) { michael@0: return; michael@0: } michael@0: var documentId = gToolboxDocument.documentElement.id; michael@0: if (aEvent.dataTransfer.types.contains("text/toolbarwrapper-id/" + documentId.toLowerCase())) michael@0: aEvent.preventDefault(); michael@0: } michael@0: michael@0: function onPaletteDrop(aEvent) michael@0: { michael@0: if (isUnwantedDragEvent(aEvent)) { michael@0: return; michael@0: } michael@0: var documentId = gToolboxDocument.documentElement.id; michael@0: var itemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId); michael@0: michael@0: var wrapper = gToolboxDocument.getElementById("wrapper-"+itemId); michael@0: if (wrapper) { michael@0: // Don't allow non-removable kids (e.g., the menubar) to move. michael@0: if (wrapper.firstChild.getAttribute("removable") != "true") michael@0: return; michael@0: michael@0: var wrapperType = wrapper.getAttribute("type"); michael@0: if (wrapperType != "separator" && michael@0: wrapperType != "spacer" && michael@0: wrapperType != "spring") { michael@0: restoreItemForToolbar(wrapper.firstChild, wrapper); michael@0: wrapPaletteItem(document.importNode(wrapper.firstChild, true)); michael@0: gToolbox.palette.appendChild(wrapper.firstChild); michael@0: } michael@0: michael@0: // The item was dragged out of the toolbar. michael@0: wrapper.parentNode.removeChild(wrapper); michael@0: } michael@0: michael@0: toolboxChanged(); michael@0: } michael@0: michael@0: michael@0: function isUnwantedDragEvent(aEvent) { michael@0: try { michael@0: if (Services.prefs.getBoolPref("toolkit.customization.unsafe_drag_events")) { michael@0: return false; michael@0: } michael@0: } catch (ex) {} michael@0: michael@0: /* Discard drag events that originated from a separate window to michael@0: prevent content->chrome privilege escalations. */ michael@0: let mozSourceNode = aEvent.dataTransfer.mozSourceNode; michael@0: // mozSourceNode is null in the dragStart event handler or if michael@0: // the drag event originated in an external application. michael@0: if (!mozSourceNode) { michael@0: return true; michael@0: } michael@0: let sourceWindow = mozSourceNode.ownerDocument.defaultView; michael@0: return sourceWindow != window && sourceWindow != gToolboxDocument.defaultView; michael@0: } michael@0: