toolkit/content/customizeToolbar.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:e977f53f07f4
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5 var gToolboxDocument = null;
6 var gToolbox = null;
7 var gCurrentDragOverItem = null;
8 var gToolboxChanged = false;
9 var gToolboxSheet = false;
10 var gPaletteBox = null;
11
12 Components.utils.import("resource://gre/modules/Services.jsm");
13
14 function onLoad()
15 {
16 if ("arguments" in window && window.arguments[0]) {
17 InitWithToolbox(window.arguments[0]);
18 repositionDialog(window);
19 }
20 else if (window.frameElement &&
21 "toolbox" in window.frameElement) {
22 gToolboxSheet = true;
23 InitWithToolbox(window.frameElement.toolbox);
24 repositionDialog(window.frameElement.panel);
25 }
26 }
27
28 function InitWithToolbox(aToolbox)
29 {
30 gToolbox = aToolbox;
31 dispatchCustomizationEvent("beforecustomization");
32 gToolboxDocument = gToolbox.ownerDocument;
33 gToolbox.customizing = true;
34 forEachCustomizableToolbar(function (toolbar) {
35 toolbar.setAttribute("customizing", "true");
36 });
37 gPaletteBox = document.getElementById("palette-box");
38
39 var elts = getRootElements();
40 for (let i=0; i < elts.length; i++) {
41 elts[i].addEventListener("dragstart", onToolbarDragStart, true);
42 elts[i].addEventListener("dragover", onToolbarDragOver, true);
43 elts[i].addEventListener("dragexit", onToolbarDragExit, true);
44 elts[i].addEventListener("drop", onToolbarDrop, true);
45 }
46
47 initDialog();
48 }
49
50 function onClose()
51 {
52 if (!gToolboxSheet)
53 window.close();
54 else
55 finishToolbarCustomization();
56 }
57
58 function onUnload()
59 {
60 if (!gToolboxSheet)
61 finishToolbarCustomization();
62 }
63
64 function finishToolbarCustomization()
65 {
66 removeToolboxListeners();
67 unwrapToolbarItems();
68 persistCurrentSets();
69 gToolbox.customizing = false;
70 forEachCustomizableToolbar(function (toolbar) {
71 toolbar.removeAttribute("customizing");
72 });
73
74 notifyParentComplete();
75 }
76
77 function initDialog()
78 {
79 if (!gToolbox.toolbarset) {
80 document.getElementById("newtoolbar").hidden = true;
81 }
82
83 var mode = gToolbox.getAttribute("mode");
84 document.getElementById("modelist").value = mode;
85 var smallIconsCheckbox = document.getElementById("smallicons");
86 smallIconsCheckbox.checked = gToolbox.getAttribute("iconsize") == "small";
87 if (mode == "text")
88 smallIconsCheckbox.disabled = true;
89
90 // Build up the palette of other items.
91 buildPalette();
92
93 // Wrap all the items on the toolbar in toolbarpaletteitems.
94 wrapToolbarItems();
95 }
96
97 function repositionDialog(aWindow)
98 {
99 // Position the dialog touching the bottom of the toolbox and centered with
100 // it.
101 if (!aWindow)
102 return;
103
104 var width;
105 if (aWindow != window)
106 width = aWindow.getBoundingClientRect().width;
107 else if (document.documentElement.hasAttribute("width"))
108 width = document.documentElement.getAttribute("width");
109 else
110 width = parseInt(document.documentElement.style.width);
111 var screenX = gToolbox.boxObject.screenX
112 + ((gToolbox.boxObject.width - width) / 2);
113 var screenY = gToolbox.boxObject.screenY + gToolbox.boxObject.height;
114
115 aWindow.moveTo(screenX, screenY);
116 }
117
118 function removeToolboxListeners()
119 {
120 var elts = getRootElements();
121 for (let i=0; i < elts.length; i++) {
122 elts[i].removeEventListener("dragstart", onToolbarDragStart, true);
123 elts[i].removeEventListener("dragover", onToolbarDragOver, true);
124 elts[i].removeEventListener("dragexit", onToolbarDragExit, true);
125 elts[i].removeEventListener("drop", onToolbarDrop, true);
126 }
127 }
128
129 /**
130 * Invoke a callback on the toolbox to notify it that the dialog is done
131 * and going away.
132 */
133 function notifyParentComplete()
134 {
135 if ("customizeDone" in gToolbox)
136 gToolbox.customizeDone(gToolboxChanged);
137 dispatchCustomizationEvent("aftercustomization");
138 }
139
140 function toolboxChanged(aType)
141 {
142 gToolboxChanged = true;
143 if ("customizeChange" in gToolbox)
144 gToolbox.customizeChange(aType);
145 dispatchCustomizationEvent("customizationchange");
146 }
147
148 function dispatchCustomizationEvent(aEventName) {
149 var evt = document.createEvent("Events");
150 evt.initEvent(aEventName, true, true);
151 gToolbox.dispatchEvent(evt);
152 }
153
154 /**
155 * Persist the current set of buttons in all customizable toolbars to
156 * localstore.
157 */
158 function persistCurrentSets()
159 {
160 if (!gToolboxChanged || gToolboxDocument.defaultView.closed)
161 return;
162
163 var customCount = 0;
164 forEachCustomizableToolbar(function (toolbar) {
165 // Calculate currentset and store it in the attribute.
166 var currentSet = toolbar.currentSet;
167 toolbar.setAttribute("currentset", currentSet);
168
169 var customIndex = toolbar.hasAttribute("customindex");
170 if (customIndex) {
171 if (!toolbar.hasChildNodes()) {
172 // Remove custom toolbars whose contents have been removed.
173 gToolbox.removeChild(toolbar);
174 } else if (gToolbox.toolbarset) {
175 // Persist custom toolbar info on the <toolbarset/>
176 gToolbox.toolbarset.setAttribute("toolbar"+(++customCount),
177 toolbar.toolbarName + ":" + currentSet);
178 gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
179 }
180 }
181
182 if (!customIndex) {
183 // Persist the currentset attribute directly on hardcoded toolbars.
184 gToolboxDocument.persist(toolbar.id, "currentset");
185 }
186 });
187
188 // Remove toolbarX attributes for removed toolbars.
189 while (gToolbox.toolbarset && gToolbox.toolbarset.hasAttribute("toolbar"+(++customCount))) {
190 gToolbox.toolbarset.removeAttribute("toolbar"+customCount);
191 gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
192 }
193 }
194
195 /**
196 * Wraps all items in all customizable toolbars in a toolbox.
197 */
198 function wrapToolbarItems()
199 {
200 forEachCustomizableToolbar(function (toolbar) {
201 Array.forEach(toolbar.childNodes, function (item) {
202 #ifdef XP_MACOSX
203 if (item.firstChild && item.firstChild.localName == "menubar")
204 return;
205 #endif
206 if (isToolbarItem(item)) {
207 let wrapper = wrapToolbarItem(item);
208 cleanupItemForToolbar(item, wrapper);
209 }
210 });
211 });
212 }
213
214 function getRootElements()
215 {
216 return [gToolbox].concat(gToolbox.externalToolbars);
217 }
218
219 /**
220 * Unwraps all items in all customizable toolbars in a toolbox.
221 */
222 function unwrapToolbarItems()
223 {
224 let elts = getRootElements();
225 for (let i=0; i < elts.length; i++) {
226 let paletteItems = elts[i].getElementsByTagName("toolbarpaletteitem");
227 let paletteItem;
228 while ((paletteItem = paletteItems.item(0)) != null) {
229 let toolbarItem = paletteItem.firstChild;
230 restoreItemForToolbar(toolbarItem, paletteItem);
231 paletteItem.parentNode.replaceChild(toolbarItem, paletteItem);
232 }
233 }
234 }
235
236 /**
237 * Creates a wrapper that can be used to contain a toolbaritem and prevent
238 * it from receiving UI events.
239 */
240 function createWrapper(aId, aDocument)
241 {
242 var wrapper = aDocument.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
243 "toolbarpaletteitem");
244
245 wrapper.id = "wrapper-"+aId;
246 return wrapper;
247 }
248
249 /**
250 * Wraps an item that has been cloned from a template and adds
251 * it to the end of the palette.
252 */
253 function wrapPaletteItem(aPaletteItem)
254 {
255 var wrapper = createWrapper(aPaletteItem.id, document);
256
257 wrapper.appendChild(aPaletteItem);
258
259 // XXX We need to call this AFTER the palette item has been appended
260 // to the wrapper or else we crash dropping certain buttons on the
261 // palette due to removal of the command and disabled attributes - JRH
262 cleanUpItemForPalette(aPaletteItem, wrapper);
263
264 gPaletteBox.appendChild(wrapper);
265 }
266
267 /**
268 * Wraps an item that is currently on a toolbar and replaces the item
269 * with the wrapper. This is not used when dropping items from the palette,
270 * only when first starting the dialog and wrapping everything on the toolbars.
271 */
272 function wrapToolbarItem(aToolbarItem)
273 {
274 var wrapper = createWrapper(aToolbarItem.id, gToolboxDocument);
275
276 wrapper.flex = aToolbarItem.flex;
277
278 aToolbarItem.parentNode.replaceChild(wrapper, aToolbarItem);
279
280 wrapper.appendChild(aToolbarItem);
281
282 return wrapper;
283 }
284
285 /**
286 * Get the list of ids for the current set of items on each toolbar.
287 */
288 function getCurrentItemIds()
289 {
290 var currentItems = {};
291 forEachCustomizableToolbar(function (toolbar) {
292 var child = toolbar.firstChild;
293 while (child) {
294 if (isToolbarItem(child))
295 currentItems[child.id] = 1;
296 child = child.nextSibling;
297 }
298 });
299 return currentItems;
300 }
301
302 /**
303 * Builds the palette of draggable items that are not yet in a toolbar.
304 */
305 function buildPalette()
306 {
307 // Empty the palette first.
308 while (gPaletteBox.lastChild)
309 gPaletteBox.removeChild(gPaletteBox.lastChild);
310
311 // Add the toolbar separator item.
312 var templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
313 "toolbarseparator");
314 templateNode.id = "separator";
315 wrapPaletteItem(templateNode);
316
317 // Add the toolbar spring item.
318 templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
319 "toolbarspring");
320 templateNode.id = "spring";
321 templateNode.flex = 1;
322 wrapPaletteItem(templateNode);
323
324 // Add the toolbar spacer item.
325 templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
326 "toolbarspacer");
327 templateNode.id = "spacer";
328 templateNode.flex = 1;
329 wrapPaletteItem(templateNode);
330
331 var currentItems = getCurrentItemIds();
332 templateNode = gToolbox.palette.firstChild;
333 while (templateNode) {
334 // Check if the item is already in a toolbar before adding it to the palette.
335 if (!(templateNode.id in currentItems)) {
336 var paletteItem = document.importNode(templateNode, true);
337 wrapPaletteItem(paletteItem);
338 }
339
340 templateNode = templateNode.nextSibling;
341 }
342 }
343
344 /**
345 * Makes sure that an item that has been cloned from a template
346 * is stripped of any attributes that may adversely affect its
347 * appearance in the palette.
348 */
349 function cleanUpItemForPalette(aItem, aWrapper)
350 {
351 aWrapper.setAttribute("place", "palette");
352 setWrapperType(aItem, aWrapper);
353
354 if (aItem.hasAttribute("title"))
355 aWrapper.setAttribute("title", aItem.getAttribute("title"));
356 else if (aItem.hasAttribute("label"))
357 aWrapper.setAttribute("title", aItem.getAttribute("label"));
358 else if (isSpecialItem(aItem)) {
359 var stringBundle = document.getElementById("stringBundle");
360 // Remove the common "toolbar" prefix to generate the string name.
361 var title = stringBundle.getString(aItem.localName.slice(7) + "Title");
362 aWrapper.setAttribute("title", title);
363 }
364 aWrapper.setAttribute("tooltiptext", aWrapper.getAttribute("title"));
365
366 // Remove attributes that screw up our appearance.
367 aItem.removeAttribute("command");
368 aItem.removeAttribute("observes");
369 aItem.removeAttribute("type");
370 aItem.removeAttribute("width");
371
372 Array.forEach(aWrapper.querySelectorAll("[disabled]"), function(aNode) {
373 aNode.removeAttribute("disabled");
374 });
375 }
376
377 /**
378 * Makes sure that an item that has been cloned from a template
379 * is stripped of all properties that may adversely affect its
380 * appearance in the toolbar. Store critical properties on the
381 * wrapper so they can be put back on the item when we're done.
382 */
383 function cleanupItemForToolbar(aItem, aWrapper)
384 {
385 setWrapperType(aItem, aWrapper);
386 aWrapper.setAttribute("place", "toolbar");
387
388 if (aItem.hasAttribute("command")) {
389 aWrapper.setAttribute("itemcommand", aItem.getAttribute("command"));
390 aItem.removeAttribute("command");
391 }
392
393 if (aItem.checked) {
394 aWrapper.setAttribute("itemchecked", "true");
395 aItem.checked = false;
396 }
397
398 if (aItem.disabled) {
399 aWrapper.setAttribute("itemdisabled", "true");
400 aItem.disabled = false;
401 }
402 }
403
404 /**
405 * Restore all the properties that we stripped off above.
406 */
407 function restoreItemForToolbar(aItem, aWrapper)
408 {
409 if (aWrapper.hasAttribute("itemdisabled"))
410 aItem.disabled = true;
411
412 if (aWrapper.hasAttribute("itemchecked"))
413 aItem.checked = true;
414
415 if (aWrapper.hasAttribute("itemcommand")) {
416 let commandID = aWrapper.getAttribute("itemcommand");
417 aItem.setAttribute("command", commandID);
418
419 //XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing
420 let command = gToolboxDocument.getElementById(commandID);
421 if (command && command.hasAttribute("disabled"))
422 aItem.setAttribute("disabled", command.getAttribute("disabled"));
423 }
424 }
425
426 function setWrapperType(aItem, aWrapper)
427 {
428 if (aItem.localName == "toolbarseparator") {
429 aWrapper.setAttribute("type", "separator");
430 } else if (aItem.localName == "toolbarspring") {
431 aWrapper.setAttribute("type", "spring");
432 } else if (aItem.localName == "toolbarspacer") {
433 aWrapper.setAttribute("type", "spacer");
434 } else if (aItem.localName == "toolbaritem" && aItem.firstChild) {
435 aWrapper.setAttribute("type", aItem.firstChild.localName);
436 }
437 }
438
439 function setDragActive(aItem, aValue)
440 {
441 var node = aItem;
442 var direction = window.getComputedStyle(aItem, null).direction;
443 var value = direction == "ltr"? "left" : "right";
444 if (aItem.localName == "toolbar") {
445 node = aItem.lastChild;
446 value = direction == "ltr"? "right" : "left";
447 }
448
449 if (!node)
450 return;
451
452 if (aValue) {
453 if (!node.hasAttribute("dragover"))
454 node.setAttribute("dragover", value);
455 } else {
456 node.removeAttribute("dragover");
457 }
458 }
459
460 function addNewToolbar()
461 {
462 var promptService = Services.prompt;
463 var stringBundle = document.getElementById("stringBundle");
464 var message = stringBundle.getString("enterToolbarName");
465 var title = stringBundle.getString("enterToolbarTitle");
466
467 var name = {};
468
469 // Quitting from the toolbar dialog while the new toolbar prompt is up
470 // can cause things to become unresponsive on the Mac. Until dialog modality
471 // is fixed (395465), disable the "Done" button explicitly.
472 var doneButton = document.getElementById("donebutton");
473 doneButton.disabled = true;
474
475 while (true) {
476
477 if (!promptService.prompt(window, title, message, name, null, {})) {
478 doneButton.disabled = false;
479 return;
480 }
481
482 if (!name.value) {
483 message = stringBundle.getFormattedString("enterToolbarBlank", [name.value]);
484 continue;
485 }
486
487 var dupeFound = false;
488
489 // Check for an existing toolbar with the same display name
490 for (let i = 0; i < gToolbox.childNodes.length; ++i) {
491 var toolbar = gToolbox.childNodes[i];
492 var toolbarName = toolbar.getAttribute("toolbarname");
493
494 if (toolbarName == name.value &&
495 toolbar.getAttribute("type") != "menubar" &&
496 toolbar.nodeName == 'toolbar') {
497 dupeFound = true;
498 break;
499 }
500 }
501
502 if (!dupeFound)
503 break;
504
505 message = stringBundle.getFormattedString("enterToolbarDup", [name.value]);
506 }
507
508 gToolbox.appendCustomToolbar(name.value, "");
509
510 toolboxChanged();
511
512 doneButton.disabled = false;
513 }
514
515 /**
516 * Restore the default set of buttons to fixed toolbars,
517 * remove all custom toolbars, and rebuild the palette.
518 */
519 function restoreDefaultSet()
520 {
521 // Unwrap the items on the toolbar.
522 unwrapToolbarItems();
523
524 // Remove all of the customized toolbars.
525 var child = gToolbox.lastChild;
526 while (child) {
527 if (child.hasAttribute("customindex")) {
528 var thisChild = child;
529 child = child.previousSibling;
530 thisChild.currentSet = "__empty";
531 gToolbox.removeChild(thisChild);
532 } else {
533 child = child.previousSibling;
534 }
535 }
536
537 // Restore the defaultset for fixed toolbars.
538 forEachCustomizableToolbar(function (toolbar) {
539 var defaultSet = toolbar.getAttribute("defaultset");
540 if (defaultSet)
541 toolbar.currentSet = defaultSet;
542 });
543
544 // Restore the default icon size and mode.
545 document.getElementById("smallicons").checked = (updateIconSize() == "small");
546 document.getElementById("modelist").value = updateToolbarMode();
547
548 // Now rebuild the palette.
549 buildPalette();
550
551 // Now re-wrap the items on the toolbar.
552 wrapToolbarItems();
553
554 toolboxChanged("reset");
555 }
556
557 function updateIconSize(aSize) {
558 return updateToolboxProperty("iconsize", aSize, "large");
559 }
560
561 function updateToolbarMode(aModeValue) {
562 var mode = updateToolboxProperty("mode", aModeValue, "icons");
563
564 var iconSizeCheckbox = document.getElementById("smallicons");
565 iconSizeCheckbox.disabled = mode == "text";
566
567 return mode;
568 }
569
570 function updateToolboxProperty(aProp, aValue, aToolkitDefault) {
571 var toolboxDefault = gToolbox.getAttribute("default" + aProp) ||
572 aToolkitDefault;
573
574 gToolbox.setAttribute(aProp, aValue || toolboxDefault);
575 gToolboxDocument.persist(gToolbox.id, aProp);
576
577 forEachCustomizableToolbar(function (toolbar) {
578 var toolbarDefault = toolbar.getAttribute("default" + aProp) ||
579 toolboxDefault;
580 if (toolbar.getAttribute("lock" + aProp) == "true" &&
581 toolbar.getAttribute(aProp) == toolbarDefault)
582 return;
583
584 toolbar.setAttribute(aProp, aValue || toolbarDefault);
585 gToolboxDocument.persist(toolbar.id, aProp);
586 });
587
588 toolboxChanged(aProp);
589
590 return aValue || toolboxDefault;
591 }
592
593 function forEachCustomizableToolbar(callback) {
594 Array.filter(gToolbox.childNodes, isCustomizableToolbar).forEach(callback);
595 Array.filter(gToolbox.externalToolbars, isCustomizableToolbar).forEach(callback);
596 }
597
598 function isCustomizableToolbar(aElt)
599 {
600 return aElt.localName == "toolbar" &&
601 aElt.getAttribute("customizable") == "true";
602 }
603
604 function isSpecialItem(aElt)
605 {
606 return aElt.localName == "toolbarseparator" ||
607 aElt.localName == "toolbarspring" ||
608 aElt.localName == "toolbarspacer";
609 }
610
611 function isToolbarItem(aElt)
612 {
613 return aElt.localName == "toolbarbutton" ||
614 aElt.localName == "toolbaritem" ||
615 aElt.localName == "toolbarseparator" ||
616 aElt.localName == "toolbarspring" ||
617 aElt.localName == "toolbarspacer";
618 }
619
620 ///////////////////////////////////////////////////////////////////////////
621 //// Drag and Drop observers
622
623 function onToolbarDragExit(aEvent)
624 {
625 if (isUnwantedDragEvent(aEvent)) {
626 return;
627 }
628
629 if (gCurrentDragOverItem)
630 setDragActive(gCurrentDragOverItem, false);
631 }
632
633 function onToolbarDragStart(aEvent)
634 {
635 var item = aEvent.target;
636 while (item && item.localName != "toolbarpaletteitem") {
637 if (item.localName == "toolbar")
638 return;
639 item = item.parentNode;
640 }
641
642 item.setAttribute("dragactive", "true");
643
644 var dt = aEvent.dataTransfer;
645 var documentId = gToolboxDocument.documentElement.id;
646 dt.setData("text/toolbarwrapper-id/" + documentId, item.firstChild.id);
647 dt.effectAllowed = "move";
648 }
649
650 function onToolbarDragOver(aEvent)
651 {
652 if (isUnwantedDragEvent(aEvent)) {
653 return;
654 }
655
656 var documentId = gToolboxDocument.documentElement.id;
657 if (!aEvent.dataTransfer.types.contains("text/toolbarwrapper-id/" + documentId.toLowerCase()))
658 return;
659
660 var toolbar = aEvent.target;
661 var dropTarget = aEvent.target;
662 while (toolbar && toolbar.localName != "toolbar") {
663 dropTarget = toolbar;
664 toolbar = toolbar.parentNode;
665 }
666
667 // Make sure we are dragging over a customizable toolbar.
668 if (!toolbar || !isCustomizableToolbar(toolbar)) {
669 gCurrentDragOverItem = null;
670 return;
671 }
672
673 var previousDragItem = gCurrentDragOverItem;
674
675 if (dropTarget.localName == "toolbar") {
676 gCurrentDragOverItem = dropTarget;
677 } else {
678 gCurrentDragOverItem = null;
679
680 var direction = window.getComputedStyle(dropTarget.parentNode, null).direction;
681 var dropTargetCenter = dropTarget.boxObject.x + (dropTarget.boxObject.width / 2);
682 var dragAfter;
683 if (direction == "ltr")
684 dragAfter = aEvent.clientX > dropTargetCenter;
685 else
686 dragAfter = aEvent.clientX < dropTargetCenter;
687
688 if (dragAfter) {
689 gCurrentDragOverItem = dropTarget.nextSibling;
690 if (!gCurrentDragOverItem)
691 gCurrentDragOverItem = toolbar;
692 } else
693 gCurrentDragOverItem = dropTarget;
694 }
695
696 if (previousDragItem && gCurrentDragOverItem != previousDragItem) {
697 setDragActive(previousDragItem, false);
698 }
699
700 setDragActive(gCurrentDragOverItem, true);
701
702 aEvent.preventDefault();
703 aEvent.stopPropagation();
704 }
705
706 function onToolbarDrop(aEvent)
707 {
708 if (isUnwantedDragEvent(aEvent)) {
709 return;
710 }
711
712 if (!gCurrentDragOverItem)
713 return;
714
715 setDragActive(gCurrentDragOverItem, false);
716
717 var documentId = gToolboxDocument.documentElement.id;
718 var draggedItemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId);
719 if (gCurrentDragOverItem.id == draggedItemId)
720 return;
721
722 var toolbar = aEvent.target;
723 while (toolbar.localName != "toolbar")
724 toolbar = toolbar.parentNode;
725
726 var draggedPaletteWrapper = document.getElementById("wrapper-"+draggedItemId);
727 if (!draggedPaletteWrapper) {
728 // The wrapper has been dragged from the toolbar.
729 // Get the wrapper from the toolbar document and make sure that
730 // it isn't being dropped on itself.
731 var wrapper = gToolboxDocument.getElementById("wrapper-"+draggedItemId);
732 if (wrapper == gCurrentDragOverItem)
733 return;
734
735 // Don't allow non-removable kids (e.g., the menubar) to move.
736 if (wrapper.firstChild.getAttribute("removable") != "true")
737 return;
738
739 // Remove the item from its place in the toolbar.
740 wrapper.parentNode.removeChild(wrapper);
741
742 // Determine which toolbar we are dropping on.
743 var dropToolbar = null;
744 if (gCurrentDragOverItem.localName == "toolbar")
745 dropToolbar = gCurrentDragOverItem;
746 else
747 dropToolbar = gCurrentDragOverItem.parentNode;
748
749 // Insert the item into the toolbar.
750 if (gCurrentDragOverItem != dropToolbar)
751 dropToolbar.insertBefore(wrapper, gCurrentDragOverItem);
752 else
753 dropToolbar.appendChild(wrapper);
754 } else {
755 // The item has been dragged from the palette
756
757 // Create a new wrapper for the item. We don't know the id yet.
758 var wrapper = createWrapper("", gToolboxDocument);
759
760 // Ask the toolbar to clone the item's template, place it inside the wrapper, and insert it in the toolbar.
761 var newItem = toolbar.insertItem(draggedItemId, gCurrentDragOverItem == toolbar ? null : gCurrentDragOverItem, wrapper);
762
763 // Prepare the item and wrapper to look good on the toolbar.
764 cleanupItemForToolbar(newItem, wrapper);
765 wrapper.id = "wrapper-"+newItem.id;
766 wrapper.flex = newItem.flex;
767
768 // Remove the wrapper from the palette.
769 if (draggedItemId != "separator" &&
770 draggedItemId != "spring" &&
771 draggedItemId != "spacer")
772 gPaletteBox.removeChild(draggedPaletteWrapper);
773 }
774
775 gCurrentDragOverItem = null;
776
777 toolboxChanged();
778 };
779
780 function onPaletteDragOver(aEvent)
781 {
782 if (isUnwantedDragEvent(aEvent)) {
783 return;
784 }
785 var documentId = gToolboxDocument.documentElement.id;
786 if (aEvent.dataTransfer.types.contains("text/toolbarwrapper-id/" + documentId.toLowerCase()))
787 aEvent.preventDefault();
788 }
789
790 function onPaletteDrop(aEvent)
791 {
792 if (isUnwantedDragEvent(aEvent)) {
793 return;
794 }
795 var documentId = gToolboxDocument.documentElement.id;
796 var itemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId);
797
798 var wrapper = gToolboxDocument.getElementById("wrapper-"+itemId);
799 if (wrapper) {
800 // Don't allow non-removable kids (e.g., the menubar) to move.
801 if (wrapper.firstChild.getAttribute("removable") != "true")
802 return;
803
804 var wrapperType = wrapper.getAttribute("type");
805 if (wrapperType != "separator" &&
806 wrapperType != "spacer" &&
807 wrapperType != "spring") {
808 restoreItemForToolbar(wrapper.firstChild, wrapper);
809 wrapPaletteItem(document.importNode(wrapper.firstChild, true));
810 gToolbox.palette.appendChild(wrapper.firstChild);
811 }
812
813 // The item was dragged out of the toolbar.
814 wrapper.parentNode.removeChild(wrapper);
815 }
816
817 toolboxChanged();
818 }
819
820
821 function isUnwantedDragEvent(aEvent) {
822 try {
823 if (Services.prefs.getBoolPref("toolkit.customization.unsafe_drag_events")) {
824 return false;
825 }
826 } catch (ex) {}
827
828 /* Discard drag events that originated from a separate window to
829 prevent content->chrome privilege escalations. */
830 let mozSourceNode = aEvent.dataTransfer.mozSourceNode;
831 // mozSourceNode is null in the dragStart event handler or if
832 // the drag event originated in an external application.
833 if (!mozSourceNode) {
834 return true;
835 }
836 let sourceWindow = mozSourceNode.ownerDocument.defaultView;
837 return sourceWindow != window && sourceWindow != gToolboxDocument.defaultView;
838 }
839

mercurial