1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/content/customizeToolbar.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,839 @@ 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 +var gToolboxDocument = null; 1.9 +var gToolbox = null; 1.10 +var gCurrentDragOverItem = null; 1.11 +var gToolboxChanged = false; 1.12 +var gToolboxSheet = false; 1.13 +var gPaletteBox = null; 1.14 + 1.15 +Components.utils.import("resource://gre/modules/Services.jsm"); 1.16 + 1.17 +function onLoad() 1.18 +{ 1.19 + if ("arguments" in window && window.arguments[0]) { 1.20 + InitWithToolbox(window.arguments[0]); 1.21 + repositionDialog(window); 1.22 + } 1.23 + else if (window.frameElement && 1.24 + "toolbox" in window.frameElement) { 1.25 + gToolboxSheet = true; 1.26 + InitWithToolbox(window.frameElement.toolbox); 1.27 + repositionDialog(window.frameElement.panel); 1.28 + } 1.29 +} 1.30 + 1.31 +function InitWithToolbox(aToolbox) 1.32 +{ 1.33 + gToolbox = aToolbox; 1.34 + dispatchCustomizationEvent("beforecustomization"); 1.35 + gToolboxDocument = gToolbox.ownerDocument; 1.36 + gToolbox.customizing = true; 1.37 + forEachCustomizableToolbar(function (toolbar) { 1.38 + toolbar.setAttribute("customizing", "true"); 1.39 + }); 1.40 + gPaletteBox = document.getElementById("palette-box"); 1.41 + 1.42 + var elts = getRootElements(); 1.43 + for (let i=0; i < elts.length; i++) { 1.44 + elts[i].addEventListener("dragstart", onToolbarDragStart, true); 1.45 + elts[i].addEventListener("dragover", onToolbarDragOver, true); 1.46 + elts[i].addEventListener("dragexit", onToolbarDragExit, true); 1.47 + elts[i].addEventListener("drop", onToolbarDrop, true); 1.48 + } 1.49 + 1.50 + initDialog(); 1.51 +} 1.52 + 1.53 +function onClose() 1.54 +{ 1.55 + if (!gToolboxSheet) 1.56 + window.close(); 1.57 + else 1.58 + finishToolbarCustomization(); 1.59 +} 1.60 + 1.61 +function onUnload() 1.62 +{ 1.63 + if (!gToolboxSheet) 1.64 + finishToolbarCustomization(); 1.65 +} 1.66 + 1.67 +function finishToolbarCustomization() 1.68 +{ 1.69 + removeToolboxListeners(); 1.70 + unwrapToolbarItems(); 1.71 + persistCurrentSets(); 1.72 + gToolbox.customizing = false; 1.73 + forEachCustomizableToolbar(function (toolbar) { 1.74 + toolbar.removeAttribute("customizing"); 1.75 + }); 1.76 + 1.77 + notifyParentComplete(); 1.78 +} 1.79 + 1.80 +function initDialog() 1.81 +{ 1.82 + if (!gToolbox.toolbarset) { 1.83 + document.getElementById("newtoolbar").hidden = true; 1.84 + } 1.85 + 1.86 + var mode = gToolbox.getAttribute("mode"); 1.87 + document.getElementById("modelist").value = mode; 1.88 + var smallIconsCheckbox = document.getElementById("smallicons"); 1.89 + smallIconsCheckbox.checked = gToolbox.getAttribute("iconsize") == "small"; 1.90 + if (mode == "text") 1.91 + smallIconsCheckbox.disabled = true; 1.92 + 1.93 + // Build up the palette of other items. 1.94 + buildPalette(); 1.95 + 1.96 + // Wrap all the items on the toolbar in toolbarpaletteitems. 1.97 + wrapToolbarItems(); 1.98 +} 1.99 + 1.100 +function repositionDialog(aWindow) 1.101 +{ 1.102 + // Position the dialog touching the bottom of the toolbox and centered with 1.103 + // it. 1.104 + if (!aWindow) 1.105 + return; 1.106 + 1.107 + var width; 1.108 + if (aWindow != window) 1.109 + width = aWindow.getBoundingClientRect().width; 1.110 + else if (document.documentElement.hasAttribute("width")) 1.111 + width = document.documentElement.getAttribute("width"); 1.112 + else 1.113 + width = parseInt(document.documentElement.style.width); 1.114 + var screenX = gToolbox.boxObject.screenX 1.115 + + ((gToolbox.boxObject.width - width) / 2); 1.116 + var screenY = gToolbox.boxObject.screenY + gToolbox.boxObject.height; 1.117 + 1.118 + aWindow.moveTo(screenX, screenY); 1.119 +} 1.120 + 1.121 +function removeToolboxListeners() 1.122 +{ 1.123 + var elts = getRootElements(); 1.124 + for (let i=0; i < elts.length; i++) { 1.125 + elts[i].removeEventListener("dragstart", onToolbarDragStart, true); 1.126 + elts[i].removeEventListener("dragover", onToolbarDragOver, true); 1.127 + elts[i].removeEventListener("dragexit", onToolbarDragExit, true); 1.128 + elts[i].removeEventListener("drop", onToolbarDrop, true); 1.129 + } 1.130 +} 1.131 + 1.132 +/** 1.133 + * Invoke a callback on the toolbox to notify it that the dialog is done 1.134 + * and going away. 1.135 + */ 1.136 +function notifyParentComplete() 1.137 +{ 1.138 + if ("customizeDone" in gToolbox) 1.139 + gToolbox.customizeDone(gToolboxChanged); 1.140 + dispatchCustomizationEvent("aftercustomization"); 1.141 +} 1.142 + 1.143 +function toolboxChanged(aType) 1.144 +{ 1.145 + gToolboxChanged = true; 1.146 + if ("customizeChange" in gToolbox) 1.147 + gToolbox.customizeChange(aType); 1.148 + dispatchCustomizationEvent("customizationchange"); 1.149 +} 1.150 + 1.151 +function dispatchCustomizationEvent(aEventName) { 1.152 + var evt = document.createEvent("Events"); 1.153 + evt.initEvent(aEventName, true, true); 1.154 + gToolbox.dispatchEvent(evt); 1.155 +} 1.156 + 1.157 +/** 1.158 + * Persist the current set of buttons in all customizable toolbars to 1.159 + * localstore. 1.160 + */ 1.161 +function persistCurrentSets() 1.162 +{ 1.163 + if (!gToolboxChanged || gToolboxDocument.defaultView.closed) 1.164 + return; 1.165 + 1.166 + var customCount = 0; 1.167 + forEachCustomizableToolbar(function (toolbar) { 1.168 + // Calculate currentset and store it in the attribute. 1.169 + var currentSet = toolbar.currentSet; 1.170 + toolbar.setAttribute("currentset", currentSet); 1.171 + 1.172 + var customIndex = toolbar.hasAttribute("customindex"); 1.173 + if (customIndex) { 1.174 + if (!toolbar.hasChildNodes()) { 1.175 + // Remove custom toolbars whose contents have been removed. 1.176 + gToolbox.removeChild(toolbar); 1.177 + } else if (gToolbox.toolbarset) { 1.178 + // Persist custom toolbar info on the <toolbarset/> 1.179 + gToolbox.toolbarset.setAttribute("toolbar"+(++customCount), 1.180 + toolbar.toolbarName + ":" + currentSet); 1.181 + gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount); 1.182 + } 1.183 + } 1.184 + 1.185 + if (!customIndex) { 1.186 + // Persist the currentset attribute directly on hardcoded toolbars. 1.187 + gToolboxDocument.persist(toolbar.id, "currentset"); 1.188 + } 1.189 + }); 1.190 + 1.191 + // Remove toolbarX attributes for removed toolbars. 1.192 + while (gToolbox.toolbarset && gToolbox.toolbarset.hasAttribute("toolbar"+(++customCount))) { 1.193 + gToolbox.toolbarset.removeAttribute("toolbar"+customCount); 1.194 + gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount); 1.195 + } 1.196 +} 1.197 + 1.198 +/** 1.199 + * Wraps all items in all customizable toolbars in a toolbox. 1.200 + */ 1.201 +function wrapToolbarItems() 1.202 +{ 1.203 + forEachCustomizableToolbar(function (toolbar) { 1.204 + Array.forEach(toolbar.childNodes, function (item) { 1.205 +#ifdef XP_MACOSX 1.206 + if (item.firstChild && item.firstChild.localName == "menubar") 1.207 + return; 1.208 +#endif 1.209 + if (isToolbarItem(item)) { 1.210 + let wrapper = wrapToolbarItem(item); 1.211 + cleanupItemForToolbar(item, wrapper); 1.212 + } 1.213 + }); 1.214 + }); 1.215 +} 1.216 + 1.217 +function getRootElements() 1.218 +{ 1.219 + return [gToolbox].concat(gToolbox.externalToolbars); 1.220 +} 1.221 + 1.222 +/** 1.223 + * Unwraps all items in all customizable toolbars in a toolbox. 1.224 + */ 1.225 +function unwrapToolbarItems() 1.226 +{ 1.227 + let elts = getRootElements(); 1.228 + for (let i=0; i < elts.length; i++) { 1.229 + let paletteItems = elts[i].getElementsByTagName("toolbarpaletteitem"); 1.230 + let paletteItem; 1.231 + while ((paletteItem = paletteItems.item(0)) != null) { 1.232 + let toolbarItem = paletteItem.firstChild; 1.233 + restoreItemForToolbar(toolbarItem, paletteItem); 1.234 + paletteItem.parentNode.replaceChild(toolbarItem, paletteItem); 1.235 + } 1.236 + } 1.237 +} 1.238 + 1.239 +/** 1.240 + * Creates a wrapper that can be used to contain a toolbaritem and prevent 1.241 + * it from receiving UI events. 1.242 + */ 1.243 +function createWrapper(aId, aDocument) 1.244 +{ 1.245 + var wrapper = aDocument.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", 1.246 + "toolbarpaletteitem"); 1.247 + 1.248 + wrapper.id = "wrapper-"+aId; 1.249 + return wrapper; 1.250 +} 1.251 + 1.252 +/** 1.253 + * Wraps an item that has been cloned from a template and adds 1.254 + * it to the end of the palette. 1.255 + */ 1.256 +function wrapPaletteItem(aPaletteItem) 1.257 +{ 1.258 + var wrapper = createWrapper(aPaletteItem.id, document); 1.259 + 1.260 + wrapper.appendChild(aPaletteItem); 1.261 + 1.262 + // XXX We need to call this AFTER the palette item has been appended 1.263 + // to the wrapper or else we crash dropping certain buttons on the 1.264 + // palette due to removal of the command and disabled attributes - JRH 1.265 + cleanUpItemForPalette(aPaletteItem, wrapper); 1.266 + 1.267 + gPaletteBox.appendChild(wrapper); 1.268 +} 1.269 + 1.270 +/** 1.271 + * Wraps an item that is currently on a toolbar and replaces the item 1.272 + * with the wrapper. This is not used when dropping items from the palette, 1.273 + * only when first starting the dialog and wrapping everything on the toolbars. 1.274 + */ 1.275 +function wrapToolbarItem(aToolbarItem) 1.276 +{ 1.277 + var wrapper = createWrapper(aToolbarItem.id, gToolboxDocument); 1.278 + 1.279 + wrapper.flex = aToolbarItem.flex; 1.280 + 1.281 + aToolbarItem.parentNode.replaceChild(wrapper, aToolbarItem); 1.282 + 1.283 + wrapper.appendChild(aToolbarItem); 1.284 + 1.285 + return wrapper; 1.286 +} 1.287 + 1.288 +/** 1.289 + * Get the list of ids for the current set of items on each toolbar. 1.290 + */ 1.291 +function getCurrentItemIds() 1.292 +{ 1.293 + var currentItems = {}; 1.294 + forEachCustomizableToolbar(function (toolbar) { 1.295 + var child = toolbar.firstChild; 1.296 + while (child) { 1.297 + if (isToolbarItem(child)) 1.298 + currentItems[child.id] = 1; 1.299 + child = child.nextSibling; 1.300 + } 1.301 + }); 1.302 + return currentItems; 1.303 +} 1.304 + 1.305 +/** 1.306 + * Builds the palette of draggable items that are not yet in a toolbar. 1.307 + */ 1.308 +function buildPalette() 1.309 +{ 1.310 + // Empty the palette first. 1.311 + while (gPaletteBox.lastChild) 1.312 + gPaletteBox.removeChild(gPaletteBox.lastChild); 1.313 + 1.314 + // Add the toolbar separator item. 1.315 + var templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", 1.316 + "toolbarseparator"); 1.317 + templateNode.id = "separator"; 1.318 + wrapPaletteItem(templateNode); 1.319 + 1.320 + // Add the toolbar spring item. 1.321 + templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", 1.322 + "toolbarspring"); 1.323 + templateNode.id = "spring"; 1.324 + templateNode.flex = 1; 1.325 + wrapPaletteItem(templateNode); 1.326 + 1.327 + // Add the toolbar spacer item. 1.328 + templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", 1.329 + "toolbarspacer"); 1.330 + templateNode.id = "spacer"; 1.331 + templateNode.flex = 1; 1.332 + wrapPaletteItem(templateNode); 1.333 + 1.334 + var currentItems = getCurrentItemIds(); 1.335 + templateNode = gToolbox.palette.firstChild; 1.336 + while (templateNode) { 1.337 + // Check if the item is already in a toolbar before adding it to the palette. 1.338 + if (!(templateNode.id in currentItems)) { 1.339 + var paletteItem = document.importNode(templateNode, true); 1.340 + wrapPaletteItem(paletteItem); 1.341 + } 1.342 + 1.343 + templateNode = templateNode.nextSibling; 1.344 + } 1.345 +} 1.346 + 1.347 +/** 1.348 + * Makes sure that an item that has been cloned from a template 1.349 + * is stripped of any attributes that may adversely affect its 1.350 + * appearance in the palette. 1.351 + */ 1.352 +function cleanUpItemForPalette(aItem, aWrapper) 1.353 +{ 1.354 + aWrapper.setAttribute("place", "palette"); 1.355 + setWrapperType(aItem, aWrapper); 1.356 + 1.357 + if (aItem.hasAttribute("title")) 1.358 + aWrapper.setAttribute("title", aItem.getAttribute("title")); 1.359 + else if (aItem.hasAttribute("label")) 1.360 + aWrapper.setAttribute("title", aItem.getAttribute("label")); 1.361 + else if (isSpecialItem(aItem)) { 1.362 + var stringBundle = document.getElementById("stringBundle"); 1.363 + // Remove the common "toolbar" prefix to generate the string name. 1.364 + var title = stringBundle.getString(aItem.localName.slice(7) + "Title"); 1.365 + aWrapper.setAttribute("title", title); 1.366 + } 1.367 + aWrapper.setAttribute("tooltiptext", aWrapper.getAttribute("title")); 1.368 + 1.369 + // Remove attributes that screw up our appearance. 1.370 + aItem.removeAttribute("command"); 1.371 + aItem.removeAttribute("observes"); 1.372 + aItem.removeAttribute("type"); 1.373 + aItem.removeAttribute("width"); 1.374 + 1.375 + Array.forEach(aWrapper.querySelectorAll("[disabled]"), function(aNode) { 1.376 + aNode.removeAttribute("disabled"); 1.377 + }); 1.378 +} 1.379 + 1.380 +/** 1.381 + * Makes sure that an item that has been cloned from a template 1.382 + * is stripped of all properties that may adversely affect its 1.383 + * appearance in the toolbar. Store critical properties on the 1.384 + * wrapper so they can be put back on the item when we're done. 1.385 + */ 1.386 +function cleanupItemForToolbar(aItem, aWrapper) 1.387 +{ 1.388 + setWrapperType(aItem, aWrapper); 1.389 + aWrapper.setAttribute("place", "toolbar"); 1.390 + 1.391 + if (aItem.hasAttribute("command")) { 1.392 + aWrapper.setAttribute("itemcommand", aItem.getAttribute("command")); 1.393 + aItem.removeAttribute("command"); 1.394 + } 1.395 + 1.396 + if (aItem.checked) { 1.397 + aWrapper.setAttribute("itemchecked", "true"); 1.398 + aItem.checked = false; 1.399 + } 1.400 + 1.401 + if (aItem.disabled) { 1.402 + aWrapper.setAttribute("itemdisabled", "true"); 1.403 + aItem.disabled = false; 1.404 + } 1.405 +} 1.406 + 1.407 +/** 1.408 + * Restore all the properties that we stripped off above. 1.409 + */ 1.410 +function restoreItemForToolbar(aItem, aWrapper) 1.411 +{ 1.412 + if (aWrapper.hasAttribute("itemdisabled")) 1.413 + aItem.disabled = true; 1.414 + 1.415 + if (aWrapper.hasAttribute("itemchecked")) 1.416 + aItem.checked = true; 1.417 + 1.418 + if (aWrapper.hasAttribute("itemcommand")) { 1.419 + let commandID = aWrapper.getAttribute("itemcommand"); 1.420 + aItem.setAttribute("command", commandID); 1.421 + 1.422 + //XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing 1.423 + let command = gToolboxDocument.getElementById(commandID); 1.424 + if (command && command.hasAttribute("disabled")) 1.425 + aItem.setAttribute("disabled", command.getAttribute("disabled")); 1.426 + } 1.427 +} 1.428 + 1.429 +function setWrapperType(aItem, aWrapper) 1.430 +{ 1.431 + if (aItem.localName == "toolbarseparator") { 1.432 + aWrapper.setAttribute("type", "separator"); 1.433 + } else if (aItem.localName == "toolbarspring") { 1.434 + aWrapper.setAttribute("type", "spring"); 1.435 + } else if (aItem.localName == "toolbarspacer") { 1.436 + aWrapper.setAttribute("type", "spacer"); 1.437 + } else if (aItem.localName == "toolbaritem" && aItem.firstChild) { 1.438 + aWrapper.setAttribute("type", aItem.firstChild.localName); 1.439 + } 1.440 +} 1.441 + 1.442 +function setDragActive(aItem, aValue) 1.443 +{ 1.444 + var node = aItem; 1.445 + var direction = window.getComputedStyle(aItem, null).direction; 1.446 + var value = direction == "ltr"? "left" : "right"; 1.447 + if (aItem.localName == "toolbar") { 1.448 + node = aItem.lastChild; 1.449 + value = direction == "ltr"? "right" : "left"; 1.450 + } 1.451 + 1.452 + if (!node) 1.453 + return; 1.454 + 1.455 + if (aValue) { 1.456 + if (!node.hasAttribute("dragover")) 1.457 + node.setAttribute("dragover", value); 1.458 + } else { 1.459 + node.removeAttribute("dragover"); 1.460 + } 1.461 +} 1.462 + 1.463 +function addNewToolbar() 1.464 +{ 1.465 + var promptService = Services.prompt; 1.466 + var stringBundle = document.getElementById("stringBundle"); 1.467 + var message = stringBundle.getString("enterToolbarName"); 1.468 + var title = stringBundle.getString("enterToolbarTitle"); 1.469 + 1.470 + var name = {}; 1.471 + 1.472 + // Quitting from the toolbar dialog while the new toolbar prompt is up 1.473 + // can cause things to become unresponsive on the Mac. Until dialog modality 1.474 + // is fixed (395465), disable the "Done" button explicitly. 1.475 + var doneButton = document.getElementById("donebutton"); 1.476 + doneButton.disabled = true; 1.477 + 1.478 + while (true) { 1.479 + 1.480 + if (!promptService.prompt(window, title, message, name, null, {})) { 1.481 + doneButton.disabled = false; 1.482 + return; 1.483 + } 1.484 + 1.485 + if (!name.value) { 1.486 + message = stringBundle.getFormattedString("enterToolbarBlank", [name.value]); 1.487 + continue; 1.488 + } 1.489 + 1.490 + var dupeFound = false; 1.491 + 1.492 + // Check for an existing toolbar with the same display name 1.493 + for (let i = 0; i < gToolbox.childNodes.length; ++i) { 1.494 + var toolbar = gToolbox.childNodes[i]; 1.495 + var toolbarName = toolbar.getAttribute("toolbarname"); 1.496 + 1.497 + if (toolbarName == name.value && 1.498 + toolbar.getAttribute("type") != "menubar" && 1.499 + toolbar.nodeName == 'toolbar') { 1.500 + dupeFound = true; 1.501 + break; 1.502 + } 1.503 + } 1.504 + 1.505 + if (!dupeFound) 1.506 + break; 1.507 + 1.508 + message = stringBundle.getFormattedString("enterToolbarDup", [name.value]); 1.509 + } 1.510 + 1.511 + gToolbox.appendCustomToolbar(name.value, ""); 1.512 + 1.513 + toolboxChanged(); 1.514 + 1.515 + doneButton.disabled = false; 1.516 +} 1.517 + 1.518 +/** 1.519 + * Restore the default set of buttons to fixed toolbars, 1.520 + * remove all custom toolbars, and rebuild the palette. 1.521 + */ 1.522 +function restoreDefaultSet() 1.523 +{ 1.524 + // Unwrap the items on the toolbar. 1.525 + unwrapToolbarItems(); 1.526 + 1.527 + // Remove all of the customized toolbars. 1.528 + var child = gToolbox.lastChild; 1.529 + while (child) { 1.530 + if (child.hasAttribute("customindex")) { 1.531 + var thisChild = child; 1.532 + child = child.previousSibling; 1.533 + thisChild.currentSet = "__empty"; 1.534 + gToolbox.removeChild(thisChild); 1.535 + } else { 1.536 + child = child.previousSibling; 1.537 + } 1.538 + } 1.539 + 1.540 + // Restore the defaultset for fixed toolbars. 1.541 + forEachCustomizableToolbar(function (toolbar) { 1.542 + var defaultSet = toolbar.getAttribute("defaultset"); 1.543 + if (defaultSet) 1.544 + toolbar.currentSet = defaultSet; 1.545 + }); 1.546 + 1.547 + // Restore the default icon size and mode. 1.548 + document.getElementById("smallicons").checked = (updateIconSize() == "small"); 1.549 + document.getElementById("modelist").value = updateToolbarMode(); 1.550 + 1.551 + // Now rebuild the palette. 1.552 + buildPalette(); 1.553 + 1.554 + // Now re-wrap the items on the toolbar. 1.555 + wrapToolbarItems(); 1.556 + 1.557 + toolboxChanged("reset"); 1.558 +} 1.559 + 1.560 +function updateIconSize(aSize) { 1.561 + return updateToolboxProperty("iconsize", aSize, "large"); 1.562 +} 1.563 + 1.564 +function updateToolbarMode(aModeValue) { 1.565 + var mode = updateToolboxProperty("mode", aModeValue, "icons"); 1.566 + 1.567 + var iconSizeCheckbox = document.getElementById("smallicons"); 1.568 + iconSizeCheckbox.disabled = mode == "text"; 1.569 + 1.570 + return mode; 1.571 +} 1.572 + 1.573 +function updateToolboxProperty(aProp, aValue, aToolkitDefault) { 1.574 + var toolboxDefault = gToolbox.getAttribute("default" + aProp) || 1.575 + aToolkitDefault; 1.576 + 1.577 + gToolbox.setAttribute(aProp, aValue || toolboxDefault); 1.578 + gToolboxDocument.persist(gToolbox.id, aProp); 1.579 + 1.580 + forEachCustomizableToolbar(function (toolbar) { 1.581 + var toolbarDefault = toolbar.getAttribute("default" + aProp) || 1.582 + toolboxDefault; 1.583 + if (toolbar.getAttribute("lock" + aProp) == "true" && 1.584 + toolbar.getAttribute(aProp) == toolbarDefault) 1.585 + return; 1.586 + 1.587 + toolbar.setAttribute(aProp, aValue || toolbarDefault); 1.588 + gToolboxDocument.persist(toolbar.id, aProp); 1.589 + }); 1.590 + 1.591 + toolboxChanged(aProp); 1.592 + 1.593 + return aValue || toolboxDefault; 1.594 +} 1.595 + 1.596 +function forEachCustomizableToolbar(callback) { 1.597 + Array.filter(gToolbox.childNodes, isCustomizableToolbar).forEach(callback); 1.598 + Array.filter(gToolbox.externalToolbars, isCustomizableToolbar).forEach(callback); 1.599 +} 1.600 + 1.601 +function isCustomizableToolbar(aElt) 1.602 +{ 1.603 + return aElt.localName == "toolbar" && 1.604 + aElt.getAttribute("customizable") == "true"; 1.605 +} 1.606 + 1.607 +function isSpecialItem(aElt) 1.608 +{ 1.609 + return aElt.localName == "toolbarseparator" || 1.610 + aElt.localName == "toolbarspring" || 1.611 + aElt.localName == "toolbarspacer"; 1.612 +} 1.613 + 1.614 +function isToolbarItem(aElt) 1.615 +{ 1.616 + return aElt.localName == "toolbarbutton" || 1.617 + aElt.localName == "toolbaritem" || 1.618 + aElt.localName == "toolbarseparator" || 1.619 + aElt.localName == "toolbarspring" || 1.620 + aElt.localName == "toolbarspacer"; 1.621 +} 1.622 + 1.623 +/////////////////////////////////////////////////////////////////////////// 1.624 +//// Drag and Drop observers 1.625 + 1.626 +function onToolbarDragExit(aEvent) 1.627 +{ 1.628 + if (isUnwantedDragEvent(aEvent)) { 1.629 + return; 1.630 + } 1.631 + 1.632 + if (gCurrentDragOverItem) 1.633 + setDragActive(gCurrentDragOverItem, false); 1.634 +} 1.635 + 1.636 +function onToolbarDragStart(aEvent) 1.637 +{ 1.638 + var item = aEvent.target; 1.639 + while (item && item.localName != "toolbarpaletteitem") { 1.640 + if (item.localName == "toolbar") 1.641 + return; 1.642 + item = item.parentNode; 1.643 + } 1.644 + 1.645 + item.setAttribute("dragactive", "true"); 1.646 + 1.647 + var dt = aEvent.dataTransfer; 1.648 + var documentId = gToolboxDocument.documentElement.id; 1.649 + dt.setData("text/toolbarwrapper-id/" + documentId, item.firstChild.id); 1.650 + dt.effectAllowed = "move"; 1.651 +} 1.652 + 1.653 +function onToolbarDragOver(aEvent) 1.654 +{ 1.655 + if (isUnwantedDragEvent(aEvent)) { 1.656 + return; 1.657 + } 1.658 + 1.659 + var documentId = gToolboxDocument.documentElement.id; 1.660 + if (!aEvent.dataTransfer.types.contains("text/toolbarwrapper-id/" + documentId.toLowerCase())) 1.661 + return; 1.662 + 1.663 + var toolbar = aEvent.target; 1.664 + var dropTarget = aEvent.target; 1.665 + while (toolbar && toolbar.localName != "toolbar") { 1.666 + dropTarget = toolbar; 1.667 + toolbar = toolbar.parentNode; 1.668 + } 1.669 + 1.670 + // Make sure we are dragging over a customizable toolbar. 1.671 + if (!toolbar || !isCustomizableToolbar(toolbar)) { 1.672 + gCurrentDragOverItem = null; 1.673 + return; 1.674 + } 1.675 + 1.676 + var previousDragItem = gCurrentDragOverItem; 1.677 + 1.678 + if (dropTarget.localName == "toolbar") { 1.679 + gCurrentDragOverItem = dropTarget; 1.680 + } else { 1.681 + gCurrentDragOverItem = null; 1.682 + 1.683 + var direction = window.getComputedStyle(dropTarget.parentNode, null).direction; 1.684 + var dropTargetCenter = dropTarget.boxObject.x + (dropTarget.boxObject.width / 2); 1.685 + var dragAfter; 1.686 + if (direction == "ltr") 1.687 + dragAfter = aEvent.clientX > dropTargetCenter; 1.688 + else 1.689 + dragAfter = aEvent.clientX < dropTargetCenter; 1.690 + 1.691 + if (dragAfter) { 1.692 + gCurrentDragOverItem = dropTarget.nextSibling; 1.693 + if (!gCurrentDragOverItem) 1.694 + gCurrentDragOverItem = toolbar; 1.695 + } else 1.696 + gCurrentDragOverItem = dropTarget; 1.697 + } 1.698 + 1.699 + if (previousDragItem && gCurrentDragOverItem != previousDragItem) { 1.700 + setDragActive(previousDragItem, false); 1.701 + } 1.702 + 1.703 + setDragActive(gCurrentDragOverItem, true); 1.704 + 1.705 + aEvent.preventDefault(); 1.706 + aEvent.stopPropagation(); 1.707 +} 1.708 + 1.709 +function onToolbarDrop(aEvent) 1.710 +{ 1.711 + if (isUnwantedDragEvent(aEvent)) { 1.712 + return; 1.713 + } 1.714 + 1.715 + if (!gCurrentDragOverItem) 1.716 + return; 1.717 + 1.718 + setDragActive(gCurrentDragOverItem, false); 1.719 + 1.720 + var documentId = gToolboxDocument.documentElement.id; 1.721 + var draggedItemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId); 1.722 + if (gCurrentDragOverItem.id == draggedItemId) 1.723 + return; 1.724 + 1.725 + var toolbar = aEvent.target; 1.726 + while (toolbar.localName != "toolbar") 1.727 + toolbar = toolbar.parentNode; 1.728 + 1.729 + var draggedPaletteWrapper = document.getElementById("wrapper-"+draggedItemId); 1.730 + if (!draggedPaletteWrapper) { 1.731 + // The wrapper has been dragged from the toolbar. 1.732 + // Get the wrapper from the toolbar document and make sure that 1.733 + // it isn't being dropped on itself. 1.734 + var wrapper = gToolboxDocument.getElementById("wrapper-"+draggedItemId); 1.735 + if (wrapper == gCurrentDragOverItem) 1.736 + return; 1.737 + 1.738 + // Don't allow non-removable kids (e.g., the menubar) to move. 1.739 + if (wrapper.firstChild.getAttribute("removable") != "true") 1.740 + return; 1.741 + 1.742 + // Remove the item from its place in the toolbar. 1.743 + wrapper.parentNode.removeChild(wrapper); 1.744 + 1.745 + // Determine which toolbar we are dropping on. 1.746 + var dropToolbar = null; 1.747 + if (gCurrentDragOverItem.localName == "toolbar") 1.748 + dropToolbar = gCurrentDragOverItem; 1.749 + else 1.750 + dropToolbar = gCurrentDragOverItem.parentNode; 1.751 + 1.752 + // Insert the item into the toolbar. 1.753 + if (gCurrentDragOverItem != dropToolbar) 1.754 + dropToolbar.insertBefore(wrapper, gCurrentDragOverItem); 1.755 + else 1.756 + dropToolbar.appendChild(wrapper); 1.757 + } else { 1.758 + // The item has been dragged from the palette 1.759 + 1.760 + // Create a new wrapper for the item. We don't know the id yet. 1.761 + var wrapper = createWrapper("", gToolboxDocument); 1.762 + 1.763 + // Ask the toolbar to clone the item's template, place it inside the wrapper, and insert it in the toolbar. 1.764 + var newItem = toolbar.insertItem(draggedItemId, gCurrentDragOverItem == toolbar ? null : gCurrentDragOverItem, wrapper); 1.765 + 1.766 + // Prepare the item and wrapper to look good on the toolbar. 1.767 + cleanupItemForToolbar(newItem, wrapper); 1.768 + wrapper.id = "wrapper-"+newItem.id; 1.769 + wrapper.flex = newItem.flex; 1.770 + 1.771 + // Remove the wrapper from the palette. 1.772 + if (draggedItemId != "separator" && 1.773 + draggedItemId != "spring" && 1.774 + draggedItemId != "spacer") 1.775 + gPaletteBox.removeChild(draggedPaletteWrapper); 1.776 + } 1.777 + 1.778 + gCurrentDragOverItem = null; 1.779 + 1.780 + toolboxChanged(); 1.781 +}; 1.782 + 1.783 +function onPaletteDragOver(aEvent) 1.784 +{ 1.785 + if (isUnwantedDragEvent(aEvent)) { 1.786 + return; 1.787 + } 1.788 + var documentId = gToolboxDocument.documentElement.id; 1.789 + if (aEvent.dataTransfer.types.contains("text/toolbarwrapper-id/" + documentId.toLowerCase())) 1.790 + aEvent.preventDefault(); 1.791 +} 1.792 + 1.793 +function onPaletteDrop(aEvent) 1.794 +{ 1.795 + if (isUnwantedDragEvent(aEvent)) { 1.796 + return; 1.797 + } 1.798 + var documentId = gToolboxDocument.documentElement.id; 1.799 + var itemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId); 1.800 + 1.801 + var wrapper = gToolboxDocument.getElementById("wrapper-"+itemId); 1.802 + if (wrapper) { 1.803 + // Don't allow non-removable kids (e.g., the menubar) to move. 1.804 + if (wrapper.firstChild.getAttribute("removable") != "true") 1.805 + return; 1.806 + 1.807 + var wrapperType = wrapper.getAttribute("type"); 1.808 + if (wrapperType != "separator" && 1.809 + wrapperType != "spacer" && 1.810 + wrapperType != "spring") { 1.811 + restoreItemForToolbar(wrapper.firstChild, wrapper); 1.812 + wrapPaletteItem(document.importNode(wrapper.firstChild, true)); 1.813 + gToolbox.palette.appendChild(wrapper.firstChild); 1.814 + } 1.815 + 1.816 + // The item was dragged out of the toolbar. 1.817 + wrapper.parentNode.removeChild(wrapper); 1.818 + } 1.819 + 1.820 + toolboxChanged(); 1.821 +} 1.822 + 1.823 + 1.824 +function isUnwantedDragEvent(aEvent) { 1.825 + try { 1.826 + if (Services.prefs.getBoolPref("toolkit.customization.unsafe_drag_events")) { 1.827 + return false; 1.828 + } 1.829 + } catch (ex) {} 1.830 + 1.831 + /* Discard drag events that originated from a separate window to 1.832 + prevent content->chrome privilege escalations. */ 1.833 + let mozSourceNode = aEvent.dataTransfer.mozSourceNode; 1.834 + // mozSourceNode is null in the dragStart event handler or if 1.835 + // the drag event originated in an external application. 1.836 + if (!mozSourceNode) { 1.837 + return true; 1.838 + } 1.839 + let sourceWindow = mozSourceNode.ownerDocument.defaultView; 1.840 + return sourceWindow != window && sourceWindow != gToolboxDocument.defaultView; 1.841 +} 1.842 +