2013-03-26 14:23:23 -07:00
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
this.EXPORTED_SYMBOLS = ["CustomizeMode"];
|
|
|
|
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
|
|
|
|
|
|
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
|
2013-09-20 05:16:33 -07:00
|
|
|
const kPrefCustomizationAnimation = "browser.uiCustomization.disableAnimation";
|
2013-03-26 14:23:23 -07:00
|
|
|
const kPaletteId = "customization-palette";
|
2013-04-10 10:37:25 -07:00
|
|
|
const kAboutURI = "about:customizing";
|
2013-05-29 13:41:44 -07:00
|
|
|
const kDragDataTypePrefix = "text/toolbarwrapper-id/";
|
2013-06-03 15:35:30 -07:00
|
|
|
const kPlaceholderClass = "panel-customization-placeholder";
|
2013-09-23 13:50:17 -07:00
|
|
|
const kSkipSourceNodePref = "browser.uiCustomization.skipSourceNodeCheck";
|
2013-11-21 13:59:49 -08:00
|
|
|
const kToolbarVisibilityBtn = "customization-toolbar-visibility-button";
|
2014-01-29 09:19:23 -08:00
|
|
|
const kDrawInTitlebarPref = "browser.tabs.drawInTitlebar";
|
2013-11-25 15:26:17 -08:00
|
|
|
const kMaxTransitionDurationMs = 2000;
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2014-02-03 02:50:23 -08:00
|
|
|
const kPanelItemContextMenu = "customizationPanelItemContextMenu";
|
|
|
|
const kPaletteItemContextMenu = "customizationPaletteItemContextMenu";
|
|
|
|
|
2013-04-30 08:00:41 -07:00
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
Cu.import("resource:///modules/CustomizableUI.jsm");
|
2013-06-03 21:02:30 -07:00
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
2013-06-17 07:37:41 -07:00
|
|
|
Cu.import("resource://gre/modules/Task.jsm");
|
|
|
|
Cu.import("resource://gre/modules/Promise.jsm");
|
2014-01-14 06:44:20 -08:00
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "DragPositionManager",
|
|
|
|
"resource:///modules/DragPositionManager.jsm");
|
2014-01-14 06:44:20 -08:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
|
|
|
|
"resource:///modules/BrowserUITelemetry.jsm");
|
2013-04-30 08:00:41 -07:00
|
|
|
|
2013-06-03 21:02:30 -07:00
|
|
|
let gModuleName = "[CustomizeMode]";
|
|
|
|
#include logging.js
|
2013-04-04 08:07:09 -07:00
|
|
|
|
2013-09-20 05:16:33 -07:00
|
|
|
let gDisableAnimation = null;
|
|
|
|
|
2014-02-03 08:38:00 -08:00
|
|
|
let gDraggingInToolbars;
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
function CustomizeMode(aWindow) {
|
2013-09-20 05:16:33 -07:00
|
|
|
if (gDisableAnimation === null) {
|
|
|
|
gDisableAnimation = Services.prefs.getPrefType(kPrefCustomizationAnimation) == Ci.nsIPrefBranch.PREF_BOOL &&
|
|
|
|
Services.prefs.getBoolPref(kPrefCustomizationAnimation);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
this.window = aWindow;
|
|
|
|
this.document = aWindow.document;
|
2013-04-10 10:37:25 -07:00
|
|
|
this.browser = aWindow.gBrowser;
|
2013-06-29 01:25:56 -07:00
|
|
|
|
|
|
|
// There are two palettes - there's the palette that can be overlayed with
|
|
|
|
// toolbar items in browser.xul. This is invisible, and never seen by the
|
|
|
|
// user. Then there's the visible palette, which gets populated and displayed
|
|
|
|
// to the user when in customizing mode.
|
|
|
|
this.visiblePalette = this.document.getElementById(kPaletteId);
|
2014-01-21 11:04:24 -08:00
|
|
|
this.paletteEmptyNotice = this.document.getElementById("customization-empty");
|
|
|
|
this.paletteSpacer = this.document.getElementById("customization-spacer");
|
2014-03-05 15:09:12 -08:00
|
|
|
this.tipPanel = this.document.getElementById("customization-tipPanel");
|
2014-01-29 09:19:23 -08:00
|
|
|
#ifdef CAN_DRAW_IN_TITLEBAR
|
|
|
|
this._updateTitlebarButton();
|
|
|
|
Services.prefs.addObserver(kDrawInTitlebarPref, this, false);
|
|
|
|
this.window.addEventListener("unload", this);
|
|
|
|
#endif
|
2013-03-26 14:23:23 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
CustomizeMode.prototype = {
|
|
|
|
_changed: false,
|
2013-06-17 07:37:41 -07:00
|
|
|
_transitioning: false,
|
2013-03-26 14:23:23 -07:00
|
|
|
window: null,
|
|
|
|
document: null,
|
|
|
|
// areas is used to cache the customizable areas when in customization mode.
|
|
|
|
areas: null,
|
|
|
|
// When in customizing mode, we swap out the reference to the invisible
|
|
|
|
// palette in gNavToolbox.palette for our visiblePalette. This way, for the
|
|
|
|
// customizing browser window, when widgets are removed from customizable
|
|
|
|
// areas and added to the palette, they're added to the visible palette.
|
|
|
|
// _stowedPalette is a reference to the old invisible palette so we can
|
|
|
|
// restore gNavToolbox.palette to its original state after exiting
|
|
|
|
// customization mode.
|
|
|
|
_stowedPalette: null,
|
2013-03-22 12:26:26 -07:00
|
|
|
_dragOverItem: null,
|
2013-04-10 10:37:25 -07:00
|
|
|
_customizing: false,
|
2013-09-23 13:50:17 -07:00
|
|
|
_skipSourceNodeCheck: null,
|
2013-11-21 20:09:34 -08:00
|
|
|
_mainViewContext: null,
|
2013-04-10 10:37:25 -07:00
|
|
|
|
2013-06-03 15:35:30 -07:00
|
|
|
get panelUIContents() {
|
|
|
|
return this.document.getElementById("PanelUI-contents");
|
|
|
|
},
|
|
|
|
|
2014-01-10 07:54:48 -08:00
|
|
|
get _handler() {
|
|
|
|
return this.window.CustomizationHandler;
|
|
|
|
},
|
|
|
|
|
2014-01-29 09:19:23 -08:00
|
|
|
uninit: function() {
|
|
|
|
#ifdef CAN_DRAW_IN_TITLEBAR
|
|
|
|
Services.prefs.removeObserver(kDrawInTitlebarPref, this);
|
|
|
|
#endif
|
|
|
|
},
|
|
|
|
|
2013-06-25 08:05:24 -07:00
|
|
|
toggle: function() {
|
2014-01-10 07:54:48 -08:00
|
|
|
if (this._handler.isEnteringCustomizeMode || this._handler.isExitingCustomizeMode) {
|
|
|
|
this._wantToBeInCustomizeMode = !this._wantToBeInCustomizeMode;
|
2013-06-25 08:05:24 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (this._customizing) {
|
|
|
|
this.exit();
|
|
|
|
} else {
|
|
|
|
this.enter();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
enter: function() {
|
2014-01-10 07:54:48 -08:00
|
|
|
this._wantToBeInCustomizeMode = true;
|
|
|
|
|
|
|
|
if (this._customizing || this._handler.isEnteringCustomizeMode) {
|
2013-04-30 08:00:41 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-10 07:54:48 -08:00
|
|
|
// Exiting; want to re-enter once we've done that.
|
|
|
|
if (this._handler.isExitingCustomizeMode) {
|
|
|
|
LOG("Attempted to enter while we're in the middle of exiting. " +
|
|
|
|
"We'll exit after we've entered");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-05-28 14:49:11 -07:00
|
|
|
// We don't need to switch to kAboutURI, or open a new tab at
|
|
|
|
// kAboutURI if we're already on it.
|
|
|
|
if (this.browser.selectedBrowser.currentURI.spec != kAboutURI) {
|
2014-03-04 07:24:00 -08:00
|
|
|
this.window.switchToTabHavingURI(kAboutURI, true, {
|
|
|
|
skipTabAnimation: true,
|
|
|
|
});
|
2013-05-28 14:49:11 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-28 11:04:51 -08:00
|
|
|
let window = this.window;
|
|
|
|
let document = this.document;
|
|
|
|
|
2014-01-10 07:54:48 -08:00
|
|
|
this._handler.isEnteringCustomizeMode = true;
|
|
|
|
|
2014-02-15 16:15:06 -08:00
|
|
|
// Always disable the reset button at the start of customize mode, it'll be re-enabled
|
|
|
|
// if necessary when we finish entering:
|
|
|
|
let resetButton = this.document.getElementById("customization-reset-button");
|
|
|
|
resetButton.setAttribute("disabled", "true");
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
Task.spawn(function() {
|
2013-10-22 04:07:30 -07:00
|
|
|
// We shouldn't start customize mode until after browser-delayed-startup has finished:
|
|
|
|
if (!this.window.gBrowserInit.delayedStartupFinished) {
|
|
|
|
let delayedStartupDeferred = Promise.defer();
|
|
|
|
let delayedStartupObserver = function(aSubject) {
|
|
|
|
if (aSubject == this.window) {
|
|
|
|
Services.obs.removeObserver(delayedStartupObserver, "browser-delayed-startup-finished");
|
|
|
|
delayedStartupDeferred.resolve();
|
|
|
|
}
|
|
|
|
}.bind(this);
|
|
|
|
Services.obs.addObserver(delayedStartupObserver, "browser-delayed-startup-finished", false);
|
|
|
|
yield delayedStartupDeferred.promise;
|
|
|
|
}
|
|
|
|
|
2013-11-21 13:59:49 -08:00
|
|
|
let toolbarVisibilityBtn = document.getElementById(kToolbarVisibilityBtn);
|
|
|
|
let togglableToolbars = window.getTogglableToolbars();
|
|
|
|
let bookmarksToolbar = document.getElementById("PersonalToolbar");
|
2014-04-23 03:04:00 -07:00
|
|
|
if (togglableToolbars.length == 0) {
|
2013-11-21 13:59:49 -08:00
|
|
|
toolbarVisibilityBtn.setAttribute("hidden", "true");
|
|
|
|
} else {
|
|
|
|
toolbarVisibilityBtn.removeAttribute("hidden");
|
|
|
|
}
|
|
|
|
|
2013-10-22 04:07:30 -07:00
|
|
|
// Disable lightweight themes while in customization mode since
|
|
|
|
// they don't have large enough images to pad the full browser window.
|
2013-11-17 13:10:14 -08:00
|
|
|
if (this.document.documentElement._lightweightTheme)
|
|
|
|
this.document.documentElement._lightweightTheme.disable();
|
2013-10-22 04:07:30 -07:00
|
|
|
|
2014-02-19 11:44:02 -08:00
|
|
|
CustomizableUI.dispatchToolboxEvent("beforecustomization", {}, window);
|
2013-11-15 04:19:07 -08:00
|
|
|
CustomizableUI.notifyStartCustomizing(this.window);
|
2013-10-22 04:07:30 -07:00
|
|
|
|
2013-11-05 12:00:30 -08:00
|
|
|
// Add a keypress listener to the document so that we can quickly exit
|
|
|
|
// customization mode when pressing ESC.
|
2013-10-28 04:16:12 -07:00
|
|
|
document.addEventListener("keypress", this);
|
2013-10-22 04:07:30 -07:00
|
|
|
|
2014-04-24 07:37:54 -07:00
|
|
|
// Same goes for the menu button - if we're customizing, a click on the
|
2013-10-22 04:07:30 -07:00
|
|
|
// menu button means a quick exit from customization mode.
|
|
|
|
window.PanelUI.hide();
|
2014-04-24 07:37:54 -07:00
|
|
|
window.PanelUI.menuButton.addEventListener("command", this);
|
2013-10-22 04:07:30 -07:00
|
|
|
window.PanelUI.menuButton.open = true;
|
|
|
|
window.PanelUI.beginBatchUpdate();
|
|
|
|
|
2014-01-31 12:05:05 -08:00
|
|
|
// The menu panel is lazy, and registers itself when the popup shows. We
|
|
|
|
// need to force the menu panel to register itself, or else customization
|
|
|
|
// is really not going to work. We pass "true" to ensureRegistered to
|
|
|
|
// indicate that we're handling calling startBatchUpdate and
|
|
|
|
// endBatchUpdate.
|
|
|
|
yield window.PanelUI.ensureReady(true);
|
|
|
|
|
2014-01-03 11:57:22 -08:00
|
|
|
// Hide the palette before starting the transition for increased perf.
|
|
|
|
this.visiblePalette.hidden = true;
|
2014-03-04 05:00:00 -08:00
|
|
|
this.visiblePalette.removeAttribute("showing");
|
2014-01-03 11:57:22 -08:00
|
|
|
|
2014-02-07 10:17:58 -08:00
|
|
|
// Disable the button-text fade-out mask
|
|
|
|
// during the transition for increased perf.
|
|
|
|
let panelContents = window.PanelUI.contents;
|
|
|
|
panelContents.setAttribute("customize-transitioning", "true");
|
|
|
|
|
2013-10-22 04:07:30 -07:00
|
|
|
// Move the mainView in the panel to the holder so that we can see it
|
|
|
|
// while customizing.
|
2013-11-21 20:09:34 -08:00
|
|
|
let mainView = window.PanelUI.mainView;
|
2014-01-03 11:57:22 -08:00
|
|
|
let panelHolder = document.getElementById("customization-panelHolder");
|
2013-11-21 20:09:34 -08:00
|
|
|
panelHolder.appendChild(mainView);
|
2013-10-22 04:07:30 -07:00
|
|
|
|
2013-12-01 21:27:39 -08:00
|
|
|
let customizeButton = document.getElementById("PanelUI-customize");
|
|
|
|
customizeButton.setAttribute("enterLabel", customizeButton.getAttribute("label"));
|
|
|
|
customizeButton.setAttribute("label", customizeButton.getAttribute("exitLabel"));
|
2014-01-29 09:10:14 -08:00
|
|
|
customizeButton.setAttribute("enterTooltiptext", customizeButton.getAttribute("tooltiptext"));
|
|
|
|
customizeButton.setAttribute("tooltiptext", customizeButton.getAttribute("exitTooltiptext"));
|
2013-12-01 21:27:39 -08:00
|
|
|
|
2013-10-22 04:07:30 -07:00
|
|
|
this._transitioning = true;
|
|
|
|
|
|
|
|
let customizer = document.getElementById("customization-container");
|
|
|
|
customizer.parentNode.selectedPanel = customizer;
|
|
|
|
customizer.hidden = false;
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
yield this._doTransition(true);
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
// Let everybody in this window know that we're about to customize.
|
2014-02-19 11:44:02 -08:00
|
|
|
CustomizableUI.dispatchToolboxEvent("customizationstarting", {}, window);
|
2013-06-17 07:37:41 -07:00
|
|
|
|
2013-11-21 20:09:34 -08:00
|
|
|
this._mainViewContext = mainView.getAttribute("context");
|
|
|
|
if (this._mainViewContext) {
|
|
|
|
mainView.removeAttribute("context");
|
|
|
|
}
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this._showPanelCustomizationPlaceholders();
|
|
|
|
|
|
|
|
yield this._wrapToolbarItems();
|
2014-04-10 10:45:33 -07:00
|
|
|
this.populatePalette();
|
2013-06-17 07:37:41 -07:00
|
|
|
|
2014-04-13 16:42:24 -07:00
|
|
|
this._addDragHandlers(this.visiblePalette);
|
2013-05-20 10:23:50 -07:00
|
|
|
|
2013-07-10 05:05:35 -07:00
|
|
|
window.gNavToolbox.addEventListener("toolbarvisibilitychange", this);
|
|
|
|
|
2014-03-10 09:56:00 -07:00
|
|
|
document.getElementById("PanelUI-help").setAttribute("disabled", true);
|
|
|
|
document.getElementById("PanelUI-quit").setAttribute("disabled", true);
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this._updateResetButton();
|
2014-02-12 09:46:26 -08:00
|
|
|
this._updateUndoResetButton();
|
2013-06-17 07:37:41 -07:00
|
|
|
|
2013-09-23 13:50:17 -07:00
|
|
|
this._skipSourceNodeCheck = Services.prefs.getPrefType(kSkipSourceNodePref) == Ci.nsIPrefBranch.PREF_BOOL &&
|
|
|
|
Services.prefs.getBoolPref(kSkipSourceNodePref);
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true]):not([collapsed=true])");
|
|
|
|
for (let toolbar of customizableToolbars)
|
|
|
|
toolbar.setAttribute("customizing", true);
|
|
|
|
|
2013-11-28 11:04:51 -08:00
|
|
|
CustomizableUI.addListener(this);
|
2013-06-17 07:37:41 -07:00
|
|
|
window.PanelUI.endBatchUpdate();
|
|
|
|
this._customizing = true;
|
|
|
|
this._transitioning = false;
|
2014-01-03 11:57:22 -08:00
|
|
|
|
|
|
|
// Show the palette now that the transition has finished.
|
|
|
|
this.visiblePalette.hidden = false;
|
2014-03-04 05:00:00 -08:00
|
|
|
window.setTimeout(() => {
|
|
|
|
// Force layout reflow to ensure the animation runs,
|
|
|
|
// and make it async so it doesn't affect the timing.
|
|
|
|
this.visiblePalette.clientTop;
|
|
|
|
this.visiblePalette.setAttribute("showing", "true");
|
|
|
|
}, 0);
|
2014-01-21 11:04:24 -08:00
|
|
|
this.paletteSpacer.hidden = true;
|
|
|
|
this._updateEmptyPaletteNotice();
|
2014-01-03 11:57:22 -08:00
|
|
|
|
2014-03-05 15:09:12 -08:00
|
|
|
this.maybeShowTip(panelHolder);
|
|
|
|
|
2014-01-10 07:54:48 -08:00
|
|
|
this._handler.isEnteringCustomizeMode = false;
|
2014-02-07 10:17:58 -08:00
|
|
|
panelContents.removeAttribute("customize-transitioning");
|
|
|
|
|
2014-02-19 11:44:02 -08:00
|
|
|
CustomizableUI.dispatchToolboxEvent("customizationready", {}, window);
|
2014-03-04 14:10:45 -08:00
|
|
|
this._enableOutlinesTimeout = window.setTimeout(() => {
|
|
|
|
this.document.getElementById("nav-bar").setAttribute("showoutline", "true");
|
|
|
|
this.panelUIContents.setAttribute("showoutline", "true");
|
|
|
|
delete this._enableOutlinesTimeout;
|
|
|
|
}, 0);
|
2014-03-04 08:48:00 -08:00
|
|
|
|
|
|
|
// It's possible that we didn't enter customize mode via the menu panel,
|
|
|
|
// meaning we didn't kick off about:customizing preloading. If that's
|
|
|
|
// the case, let's kick it off for the next time we load this mode.
|
|
|
|
window.gCustomizationTabPreloader.ensurePreloading();
|
2014-01-10 07:54:48 -08:00
|
|
|
if (!this._wantToBeInCustomizeMode) {
|
|
|
|
this.exit();
|
|
|
|
}
|
2013-11-28 11:04:51 -08:00
|
|
|
}.bind(this)).then(null, function(e) {
|
|
|
|
ERROR(e);
|
|
|
|
// We should ensure this has been called, and calling it again doesn't hurt:
|
|
|
|
window.PanelUI.endBatchUpdate();
|
2014-01-10 07:54:48 -08:00
|
|
|
this._handler.isEnteringCustomizeMode = false;
|
|
|
|
}.bind(this));
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2013-04-10 10:37:25 -07:00
|
|
|
exit: function() {
|
2014-01-10 07:54:48 -08:00
|
|
|
this._wantToBeInCustomizeMode = false;
|
|
|
|
|
|
|
|
if (!this._customizing || this._handler.isExitingCustomizeMode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Entering; want to exit once we've done that.
|
|
|
|
if (this._handler.isEnteringCustomizeMode) {
|
|
|
|
LOG("Attempted to exit while we're in the middle of entering. " +
|
|
|
|
"We'll exit after we've entered");
|
2013-04-30 08:00:41 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-26 09:55:00 -08:00
|
|
|
if (this.resetting) {
|
|
|
|
LOG("Attempted to exit while we're resetting. " +
|
|
|
|
"We'll exit after resetting has finished.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-03-05 15:09:12 -08:00
|
|
|
this.hideTip();
|
|
|
|
|
2014-01-10 07:54:48 -08:00
|
|
|
this._handler.isExitingCustomizeMode = true;
|
|
|
|
|
2014-03-04 14:10:45 -08:00
|
|
|
if (this._enableOutlinesTimeout) {
|
|
|
|
this.window.clearTimeout(this._enableOutlinesTimeout);
|
|
|
|
} else {
|
|
|
|
this.document.getElementById("nav-bar").removeAttribute("showoutline");
|
|
|
|
this.panelUIContents.removeAttribute("showoutline");
|
|
|
|
}
|
|
|
|
|
2014-03-03 13:18:28 -08:00
|
|
|
this._removeExtraToolbarsIfEmpty();
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
CustomizableUI.removeListener(this);
|
|
|
|
|
2013-10-28 04:16:12 -07:00
|
|
|
this.document.removeEventListener("keypress", this);
|
2014-04-24 07:37:54 -07:00
|
|
|
this.window.PanelUI.menuButton.removeEventListener("command", this);
|
2014-03-11 09:44:18 -07:00
|
|
|
this.window.PanelUI.menuButton.open = false;
|
2013-03-22 12:15:58 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this.window.PanelUI.beginBatchUpdate();
|
|
|
|
|
2013-06-03 15:35:30 -07:00
|
|
|
this._removePanelCustomizationPlaceholders();
|
2013-03-26 14:23:23 -07:00
|
|
|
|
|
|
|
let window = this.window;
|
|
|
|
let document = this.document;
|
2013-05-10 13:24:18 -07:00
|
|
|
let documentElement = document.documentElement;
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2014-01-03 11:57:22 -08:00
|
|
|
// Hide the palette before starting the transition for increased perf.
|
2014-01-21 11:04:24 -08:00
|
|
|
this.paletteSpacer.hidden = false;
|
2014-01-03 11:57:22 -08:00
|
|
|
this.visiblePalette.hidden = true;
|
2014-03-04 05:00:00 -08:00
|
|
|
this.visiblePalette.removeAttribute("showing");
|
2014-01-21 11:04:24 -08:00
|
|
|
this.paletteEmptyNotice.hidden = true;
|
2014-01-03 11:57:22 -08:00
|
|
|
|
2014-02-07 10:17:58 -08:00
|
|
|
// Disable the button-text fade-out mask
|
|
|
|
// during the transition for increased perf.
|
|
|
|
let panelContents = window.PanelUI.contents;
|
|
|
|
panelContents.setAttribute("customize-transitioning", "true");
|
|
|
|
|
2014-02-15 16:15:06 -08:00
|
|
|
// Disable the reset and undo reset buttons while transitioning:
|
|
|
|
let resetButton = this.document.getElementById("customization-reset-button");
|
|
|
|
let undoResetButton = this.document.getElementById("customization-undo-reset-button");
|
|
|
|
undoResetButton.hidden = resetButton.disabled = true;
|
|
|
|
|
2014-01-03 11:57:22 -08:00
|
|
|
this._transitioning = true;
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
Task.spawn(function() {
|
2013-08-21 09:24:36 -07:00
|
|
|
yield this.depopulatePalette();
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
yield this._doTransition(false);
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
let browser = document.getElementById("browser");
|
2014-03-04 04:18:00 -08:00
|
|
|
if (this.browser.selectedBrowser.currentURI.spec == kAboutURI) {
|
|
|
|
let custBrowser = this.browser.selectedBrowser;
|
|
|
|
if (custBrowser.canGoBack) {
|
|
|
|
// If there's history to this tab, just go back.
|
|
|
|
// Note that this throws an exception if the previous document has a
|
|
|
|
// problematic URL (e.g. about:idontexist)
|
|
|
|
try {
|
|
|
|
custBrowser.goBack();
|
|
|
|
} catch (ex) {
|
|
|
|
ERROR(ex);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If we can't go back, we're removing the about:customization tab.
|
|
|
|
// We only do this if we're the top window for this window (so not
|
|
|
|
// a dialog window, for example).
|
|
|
|
if (window.getTopWin(true) == window) {
|
|
|
|
let customizationTab = this.browser.selectedTab;
|
|
|
|
if (this.browser.browsers.length == 1) {
|
|
|
|
window.BrowserOpenTab();
|
|
|
|
}
|
|
|
|
this.browser.removeTab(customizationTab);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-06-17 07:37:41 -07:00
|
|
|
browser.parentNode.selectedPanel = browser;
|
2014-03-04 04:18:00 -08:00
|
|
|
let customizer = document.getElementById("customization-container");
|
|
|
|
customizer.hidden = true;
|
2013-05-09 09:00:31 -07:00
|
|
|
|
2013-07-10 05:05:35 -07:00
|
|
|
window.gNavToolbox.removeEventListener("toolbarvisibilitychange", this);
|
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
DragPositionManager.stop();
|
2014-04-13 16:42:24 -07:00
|
|
|
this._removeDragHandlers(this.visiblePalette);
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
yield this._unwrapToolbarItems();
|
2013-04-10 10:37:25 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
if (this._changed) {
|
|
|
|
// XXXmconley: At first, it seems strange to also persist the old way with
|
|
|
|
// currentset - but this might actually be useful for switching
|
|
|
|
// to old builds. We might want to keep this around for a little
|
|
|
|
// bit.
|
|
|
|
this.persistCurrentSets();
|
|
|
|
}
|
2013-05-07 13:08:36 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
// And drop all area references.
|
|
|
|
this.areas = [];
|
|
|
|
|
|
|
|
// Let everybody in this window know that we're starting to
|
|
|
|
// exit customization mode.
|
2014-02-19 11:44:02 -08:00
|
|
|
CustomizableUI.dispatchToolboxEvent("customizationending", {}, window);
|
2013-09-17 15:20:19 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
window.PanelUI.setMainView(window.PanelUI.mainView);
|
|
|
|
window.PanelUI.menuButton.disabled = false;
|
|
|
|
|
2013-12-01 21:27:39 -08:00
|
|
|
let customizeButton = document.getElementById("PanelUI-customize");
|
|
|
|
customizeButton.setAttribute("exitLabel", customizeButton.getAttribute("label"));
|
|
|
|
customizeButton.setAttribute("label", customizeButton.getAttribute("enterLabel"));
|
2014-01-29 09:10:14 -08:00
|
|
|
customizeButton.setAttribute("exitTooltiptext", customizeButton.getAttribute("tooltiptext"));
|
|
|
|
customizeButton.setAttribute("tooltiptext", customizeButton.getAttribute("enterTooltiptext"));
|
2013-12-01 21:27:39 -08:00
|
|
|
|
2013-09-17 15:20:19 -07:00
|
|
|
// We have to use setAttribute/removeAttribute here instead of the
|
|
|
|
// property because the XBL property will be set later, and right
|
|
|
|
// now we'd be setting an expando, which breaks the XBL property.
|
|
|
|
document.getElementById("PanelUI-help").removeAttribute("disabled");
|
|
|
|
document.getElementById("PanelUI-quit").removeAttribute("disabled");
|
2013-06-25 08:05:24 -07:00
|
|
|
|
2014-02-07 10:17:58 -08:00
|
|
|
panelContents.removeAttribute("customize-transitioning");
|
|
|
|
|
2013-09-17 15:20:19 -07:00
|
|
|
// We need to set this._customizing to false before removing the tab
|
2013-06-17 07:37:41 -07:00
|
|
|
// or the TabSelect event handler will think that we are exiting
|
|
|
|
// customization mode for a second time.
|
|
|
|
this._customizing = false;
|
|
|
|
|
2013-11-21 20:09:34 -08:00
|
|
|
let mainView = window.PanelUI.mainView;
|
|
|
|
if (this._mainViewContext) {
|
|
|
|
mainView.setAttribute("context", this._mainViewContext);
|
|
|
|
}
|
|
|
|
|
2013-11-17 13:10:14 -08:00
|
|
|
if (this.document.documentElement._lightweightTheme)
|
|
|
|
this.document.documentElement._lightweightTheme.enable();
|
2013-05-17 12:29:05 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true])");
|
|
|
|
for (let toolbar of customizableToolbars)
|
|
|
|
toolbar.removeAttribute("customizing");
|
2013-05-23 06:40:55 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this.window.PanelUI.endBatchUpdate();
|
|
|
|
this._changed = false;
|
|
|
|
this._transitioning = false;
|
2014-01-10 07:54:48 -08:00
|
|
|
this._handler.isExitingCustomizeMode = false;
|
2014-02-19 11:44:02 -08:00
|
|
|
CustomizableUI.dispatchToolboxEvent("aftercustomization", {}, window);
|
|
|
|
CustomizableUI.notifyEndCustomizing(window);
|
2014-01-10 07:54:48 -08:00
|
|
|
|
|
|
|
if (this._wantToBeInCustomizeMode) {
|
|
|
|
this.enter();
|
|
|
|
}
|
2013-11-28 11:04:51 -08:00
|
|
|
}.bind(this)).then(null, function(e) {
|
|
|
|
ERROR(e);
|
|
|
|
// We should ensure this has been called, and calling it again doesn't hurt:
|
|
|
|
window.PanelUI.endBatchUpdate();
|
2014-01-10 07:54:48 -08:00
|
|
|
this._handler.isExitingCustomizeMode = false;
|
|
|
|
}.bind(this));
|
2013-06-17 07:37:41 -07:00
|
|
|
},
|
|
|
|
|
2013-11-06 20:42:31 -08:00
|
|
|
/**
|
|
|
|
* The customize mode transition has 3 phases when entering:
|
|
|
|
* 1) Pre-customization mode
|
|
|
|
* This is the starting phase of the browser.
|
|
|
|
* 2) customize-entering
|
|
|
|
* This phase is a transition, optimized for smoothness.
|
|
|
|
* 3) customize-entered
|
|
|
|
* After the transition completes, this phase draws all of the
|
|
|
|
* expensive detail that isn't necessary during the second phase.
|
|
|
|
*
|
|
|
|
* Exiting customization mode has a similar set of phases, but in reverse
|
|
|
|
* order - customize-entered, customize-exiting, pre-customization mode.
|
|
|
|
*
|
|
|
|
* When in the customize-entering, customize-entered, or customize-exiting
|
|
|
|
* phases, there is a "customizing" attribute set on the main-window to simplify
|
|
|
|
* excluding certain styles while in any phase of customize mode.
|
|
|
|
*/
|
2013-06-17 07:37:41 -07:00
|
|
|
_doTransition: function(aEntering) {
|
|
|
|
let deferred = Promise.defer();
|
2014-01-31 08:28:35 -08:00
|
|
|
let deck = this.document.getElementById("content-deck");
|
2013-11-06 20:42:31 -08:00
|
|
|
|
2013-08-20 04:39:04 -07:00
|
|
|
let customizeTransitionEnd = function(aEvent) {
|
2013-11-25 15:26:17 -08:00
|
|
|
if (aEvent != "timedout" &&
|
2014-01-31 08:28:35 -08:00
|
|
|
(aEvent.originalTarget != deck || aEvent.propertyName != "margin-left")) {
|
2013-06-17 07:37:41 -07:00
|
|
|
return;
|
|
|
|
}
|
2013-11-25 15:26:17 -08:00
|
|
|
this.window.clearTimeout(catchAllTimeout);
|
2014-01-31 08:28:35 -08:00
|
|
|
// Bug 962677: We let the event loop breathe for before we do the final
|
|
|
|
// stage of the transition to improve perceived performance.
|
|
|
|
this.window.setTimeout(function () {
|
|
|
|
deck.removeEventListener("transitionend", customizeTransitionEnd);
|
|
|
|
|
|
|
|
if (!aEntering) {
|
|
|
|
this.document.documentElement.removeAttribute("customize-exiting");
|
|
|
|
this.document.documentElement.removeAttribute("customizing");
|
|
|
|
} else {
|
|
|
|
this.document.documentElement.setAttribute("customize-entered", true);
|
|
|
|
this.document.documentElement.removeAttribute("customize-entering");
|
|
|
|
}
|
2014-02-19 11:44:02 -08:00
|
|
|
CustomizableUI.dispatchToolboxEvent("customization-transitionend", aEntering, this.window);
|
2013-06-17 07:37:41 -07:00
|
|
|
|
2014-01-31 08:28:35 -08:00
|
|
|
deferred.resolve();
|
|
|
|
}.bind(this), 0);
|
2013-08-20 04:39:04 -07:00
|
|
|
}.bind(this);
|
|
|
|
deck.addEventListener("transitionend", customizeTransitionEnd);
|
2013-06-17 07:37:41 -07:00
|
|
|
|
2013-09-20 05:16:33 -07:00
|
|
|
if (gDisableAnimation) {
|
2014-02-28 07:06:12 -08:00
|
|
|
this.document.getElementById("tab-view-deck").setAttribute("fastcustomizeanimation", true);
|
2013-09-20 05:16:33 -07:00
|
|
|
}
|
2013-06-17 07:37:41 -07:00
|
|
|
if (aEntering) {
|
|
|
|
this.document.documentElement.setAttribute("customizing", true);
|
2013-11-06 20:42:31 -08:00
|
|
|
this.document.documentElement.setAttribute("customize-entering", true);
|
2013-06-17 07:37:41 -07:00
|
|
|
} else {
|
|
|
|
this.document.documentElement.setAttribute("customize-exiting", true);
|
2013-11-06 20:42:31 -08:00
|
|
|
this.document.documentElement.removeAttribute("customize-entered");
|
2013-06-17 07:37:41 -07:00
|
|
|
}
|
2013-11-25 15:26:17 -08:00
|
|
|
|
|
|
|
let catchAll = () => customizeTransitionEnd("timedout");
|
|
|
|
let catchAllTimeout = this.window.setTimeout(catchAll, kMaxTransitionDurationMs);
|
2013-06-17 07:37:41 -07:00
|
|
|
return deferred.promise;
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2014-03-05 15:09:12 -08:00
|
|
|
maybeShowTip: function(aAnchor) {
|
|
|
|
let shown = false;
|
|
|
|
const kShownPref = "browser.customizemode.tip0.shown";
|
|
|
|
try {
|
|
|
|
shown = Services.prefs.getBoolPref(kShownPref);
|
|
|
|
} catch (ex) {}
|
|
|
|
if (shown)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let anchorNode = aAnchor || this.document.getElementById("customization-panelHolder");
|
|
|
|
let messageNode = this.tipPanel.querySelector(".customization-tipPanel-contentMessage");
|
|
|
|
if (!messageNode.childElementCount) {
|
|
|
|
// Put the tip contents in the popup.
|
|
|
|
let bundle = this.document.getElementById("bundle_browser");
|
|
|
|
const kLabelClass = "customization-tipPanel-link";
|
|
|
|
messageNode.innerHTML = bundle.getFormattedString("customizeTips.tip0", [
|
|
|
|
"<label class=\"customization-tipPanel-em\" value=\"" +
|
|
|
|
bundle.getString("customizeTips.tip0.hint") + "\"/>",
|
|
|
|
this.document.getElementById("bundle_brand").getString("brandShortName"),
|
|
|
|
"<label class=\"" + kLabelClass + " text-link\" value=\"" +
|
|
|
|
bundle.getString("customizeTips.tip0.learnMore") + "\"/>"
|
|
|
|
]);
|
|
|
|
|
|
|
|
messageNode.querySelector("." + kLabelClass).addEventListener("click", () => {
|
|
|
|
let url = Services.urlFormatter.formatURLPref("browser.customizemode.tip0.learnMoreUrl");
|
|
|
|
let browser = this.browser;
|
|
|
|
browser.selectedTab = browser.addTab(url);
|
|
|
|
this.hideTip();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-03-06 14:06:30 -08:00
|
|
|
this.tipPanel.hidden = false;
|
2014-03-05 15:09:12 -08:00
|
|
|
this.tipPanel.openPopup(anchorNode);
|
|
|
|
Services.prefs.setBoolPref(kShownPref, true);
|
|
|
|
},
|
|
|
|
|
|
|
|
hideTip: function() {
|
|
|
|
this.tipPanel.hidePopup();
|
|
|
|
},
|
|
|
|
|
2013-11-25 15:24:33 -08:00
|
|
|
_getCustomizableChildForNode: function(aNode) {
|
2014-02-12 09:32:23 -08:00
|
|
|
// NB: adjusted from _getCustomizableParent to keep that method fast
|
|
|
|
// (it's used during drags), and avoid multiple DOM loops
|
|
|
|
let areas = CustomizableUI.areas;
|
|
|
|
// Caching this length is important because otherwise we'll also iterate
|
|
|
|
// over items we add to the end from within the loop.
|
|
|
|
let numberOfAreas = areas.length;
|
|
|
|
for (let i = 0; i < numberOfAreas; i++) {
|
|
|
|
let area = areas[i];
|
|
|
|
let areaNode = aNode.ownerDocument.getElementById(area);
|
|
|
|
let customizationTarget = areaNode && areaNode.customizationTarget;
|
|
|
|
if (customizationTarget && customizationTarget != areaNode) {
|
|
|
|
areas.push(customizationTarget.id);
|
|
|
|
}
|
|
|
|
let overflowTarget = areaNode.getAttribute("overflowtarget");
|
|
|
|
if (overflowTarget) {
|
|
|
|
areas.push(overflowTarget);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
areas.push(kPaletteId);
|
|
|
|
|
|
|
|
while (aNode && aNode.parentNode) {
|
|
|
|
let parent = aNode.parentNode;
|
|
|
|
if (areas.indexOf(parent.id) != -1) {
|
|
|
|
return aNode;
|
|
|
|
}
|
|
|
|
aNode = parent;
|
2013-11-25 15:24:33 -08:00
|
|
|
}
|
2014-02-12 09:32:23 -08:00
|
|
|
return null;
|
2013-11-25 15:24:33 -08:00
|
|
|
},
|
|
|
|
|
2013-06-05 02:31:37 -07:00
|
|
|
addToToolbar: function(aNode) {
|
2013-11-25 15:24:33 -08:00
|
|
|
aNode = this._getCustomizableChildForNode(aNode);
|
2013-11-21 20:09:34 -08:00
|
|
|
if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
|
|
|
|
aNode = aNode.firstChild;
|
|
|
|
}
|
2013-06-05 02:31:37 -07:00
|
|
|
CustomizableUI.addWidgetToArea(aNode.id, CustomizableUI.AREA_NAVBAR);
|
2014-01-02 12:32:09 -08:00
|
|
|
if (!this._customizing) {
|
2014-02-19 11:44:02 -08:00
|
|
|
CustomizableUI.dispatchToolboxEvent("customizationchange");
|
2014-01-02 12:32:09 -08:00
|
|
|
}
|
2013-06-05 02:31:37 -07:00
|
|
|
},
|
|
|
|
|
2013-11-21 20:09:34 -08:00
|
|
|
addToPanel: function(aNode) {
|
2013-11-25 15:24:33 -08:00
|
|
|
aNode = this._getCustomizableChildForNode(aNode);
|
2013-11-21 20:09:34 -08:00
|
|
|
if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
|
|
|
|
aNode = aNode.firstChild;
|
|
|
|
}
|
|
|
|
CustomizableUI.addWidgetToArea(aNode.id, CustomizableUI.AREA_PANEL);
|
2014-01-02 12:32:09 -08:00
|
|
|
if (!this._customizing) {
|
2014-02-19 11:44:02 -08:00
|
|
|
CustomizableUI.dispatchToolboxEvent("customizationchange");
|
2014-01-02 12:32:09 -08:00
|
|
|
}
|
2013-11-21 20:09:34 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
removeFromArea: function(aNode) {
|
2013-11-25 15:24:33 -08:00
|
|
|
aNode = this._getCustomizableChildForNode(aNode);
|
2013-11-21 20:09:34 -08:00
|
|
|
if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
|
|
|
|
aNode = aNode.firstChild;
|
|
|
|
}
|
2013-06-05 02:31:37 -07:00
|
|
|
CustomizableUI.removeWidgetFromArea(aNode.id);
|
2014-01-02 12:32:09 -08:00
|
|
|
if (!this._customizing) {
|
2014-02-19 11:44:02 -08:00
|
|
|
CustomizableUI.dispatchToolboxEvent("customizationchange");
|
2014-01-02 12:32:09 -08:00
|
|
|
}
|
2013-06-05 02:31:37 -07:00
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
populatePalette: function() {
|
2013-06-17 07:37:41 -07:00
|
|
|
let fragment = this.document.createDocumentFragment();
|
2013-03-26 14:23:23 -07:00
|
|
|
let toolboxPalette = this.window.gNavToolbox.palette;
|
|
|
|
|
2014-04-10 10:45:33 -07:00
|
|
|
try {
|
2013-06-17 07:37:41 -07:00
|
|
|
let unusedWidgets = CustomizableUI.getUnusedWidgets(toolboxPalette);
|
|
|
|
for (let widget of unusedWidgets) {
|
|
|
|
let paletteItem = this.makePaletteItem(widget, "palette");
|
|
|
|
fragment.appendChild(paletteItem);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this.visiblePalette.appendChild(fragment);
|
|
|
|
this._stowedPalette = this.window.gNavToolbox.palette;
|
|
|
|
this.window.gNavToolbox.palette = this.visiblePalette;
|
2014-04-10 10:45:33 -07:00
|
|
|
} catch (ex) {
|
|
|
|
ERROR(ex);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
//XXXunf Maybe this should use -moz-element instead of wrapping the node?
|
|
|
|
// Would ensure no weird interactions/event handling from original node,
|
|
|
|
// and makes it possible to put this in a lazy-loaded iframe/real tab
|
|
|
|
// while still getting rid of the need for overlays.
|
|
|
|
makePaletteItem: function(aWidget, aPlace) {
|
|
|
|
let widgetNode = aWidget.forWindow(this.window).node;
|
2014-03-11 05:29:25 -07:00
|
|
|
let wrapper = this.createOrUpdateWrapper(widgetNode, aPlace);
|
2013-03-26 14:23:23 -07:00
|
|
|
wrapper.appendChild(widgetNode);
|
|
|
|
return wrapper;
|
|
|
|
},
|
|
|
|
|
|
|
|
depopulatePalette: function() {
|
2013-06-17 07:37:41 -07:00
|
|
|
return Task.spawn(function() {
|
|
|
|
this.visiblePalette.hidden = true;
|
|
|
|
let paletteChild = this.visiblePalette.firstChild;
|
|
|
|
let nextChild;
|
|
|
|
while (paletteChild) {
|
|
|
|
nextChild = paletteChild.nextElementSibling;
|
|
|
|
let provider = CustomizableUI.getWidget(paletteChild.id).provider;
|
|
|
|
if (provider == CustomizableUI.PROVIDER_XUL) {
|
|
|
|
let unwrappedPaletteItem =
|
|
|
|
yield this.deferredUnwrapToolbarItem(paletteChild);
|
|
|
|
this._stowedPalette.appendChild(unwrappedPaletteItem);
|
|
|
|
} else if (provider == CustomizableUI.PROVIDER_API) {
|
|
|
|
//XXXunf Currently this doesn't destroy the (now unused) node. It would
|
|
|
|
// be good to do so, but we need to keep strong refs to it in
|
|
|
|
// CustomizableUI (can't iterate of WeakMaps), and there's the
|
|
|
|
// question of what behavior wrappers should have if consumers
|
|
|
|
// keep hold of them.
|
|
|
|
//widget.destroyInstance(widgetNode);
|
|
|
|
} else if (provider == CustomizableUI.PROVIDER_SPECIAL) {
|
|
|
|
this.visiblePalette.removeChild(paletteChild);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
paletteChild = nextChild;
|
|
|
|
}
|
|
|
|
this.visiblePalette.hidden = false;
|
|
|
|
this.window.gNavToolbox.palette = this._stowedPalette;
|
2013-08-21 12:33:46 -07:00
|
|
|
}.bind(this)).then(null, ERROR);
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2013-04-30 08:00:41 -07:00
|
|
|
isCustomizableItem: function(aNode) {
|
|
|
|
return aNode.localName == "toolbarbutton" ||
|
|
|
|
aNode.localName == "toolbaritem" ||
|
|
|
|
aNode.localName == "toolbarseparator" ||
|
|
|
|
aNode.localName == "toolbarspring" ||
|
|
|
|
aNode.localName == "toolbarspacer";
|
|
|
|
},
|
|
|
|
|
|
|
|
isWrappedToolbarItem: function(aNode) {
|
|
|
|
return aNode.localName == "toolbarpaletteitem";
|
|
|
|
},
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
deferredWrapToolbarItem: function(aNode, aPlace) {
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
|
|
|
|
dispatchFunction(function() {
|
|
|
|
let wrapper = this.wrapToolbarItem(aNode, aPlace);
|
|
|
|
deferred.resolve(wrapper);
|
|
|
|
}.bind(this))
|
|
|
|
|
|
|
|
return deferred.promise;
|
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
wrapToolbarItem: function(aNode, aPlace) {
|
2013-05-30 01:07:43 -07:00
|
|
|
if (!this.isCustomizableItem(aNode)) {
|
|
|
|
return aNode;
|
|
|
|
}
|
2014-03-11 05:29:25 -07:00
|
|
|
let wrapper = this.createOrUpdateWrapper(aNode, aPlace);
|
|
|
|
|
2013-03-22 12:15:58 -07:00
|
|
|
// It's possible that this toolbar node is "mid-flight" and doesn't have
|
|
|
|
// a parent, in which case we skip replacing it. This can happen if a
|
|
|
|
// toolbar item has been dragged into the palette. In that case, we tell
|
|
|
|
// CustomizableUI to remove the widget from its area before putting the
|
|
|
|
// widget in the palette - so the node will have no parent.
|
|
|
|
if (aNode.parentNode) {
|
|
|
|
aNode = aNode.parentNode.replaceChild(wrapper, aNode);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
wrapper.appendChild(aNode);
|
|
|
|
return wrapper;
|
|
|
|
},
|
|
|
|
|
2014-03-11 05:29:25 -07:00
|
|
|
createOrUpdateWrapper: function(aNode, aPlace, aIsUpdate) {
|
|
|
|
let wrapper;
|
|
|
|
if (aIsUpdate && aNode.parentNode && aNode.parentNode.localName == "toolbarpaletteitem") {
|
|
|
|
wrapper = aNode.parentNode;
|
|
|
|
aPlace = wrapper.getAttribute("place");
|
|
|
|
} else {
|
|
|
|
wrapper = this.document.createElement("toolbarpaletteitem");
|
|
|
|
// "place" is used by toolkit to add the toolbarpaletteitem-palette
|
|
|
|
// binding to a toolbarpaletteitem, which gives it a label node for when
|
|
|
|
// it's sitting in the palette.
|
|
|
|
wrapper.setAttribute("place", aPlace);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
|
|
|
|
|
|
|
|
// Ensure the wrapped item doesn't look like it's in any special state, and
|
|
|
|
// can't be interactved with when in the customization palette.
|
|
|
|
if (aNode.hasAttribute("command")) {
|
|
|
|
wrapper.setAttribute("itemcommand", aNode.getAttribute("command"));
|
|
|
|
aNode.removeAttribute("command");
|
|
|
|
}
|
|
|
|
|
2013-12-16 20:20:13 -08:00
|
|
|
if (aNode.hasAttribute("observes")) {
|
|
|
|
wrapper.setAttribute("itemobserves", aNode.getAttribute("observes"));
|
|
|
|
aNode.removeAttribute("observes");
|
|
|
|
}
|
|
|
|
|
2014-01-03 11:57:22 -08:00
|
|
|
if (aNode.getAttribute("checked") == "true") {
|
2013-03-26 14:23:23 -07:00
|
|
|
wrapper.setAttribute("itemchecked", "true");
|
2014-01-03 11:57:22 -08:00
|
|
|
aNode.removeAttribute("checked");
|
2013-03-26 14:23:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (aNode.hasAttribute("id")) {
|
|
|
|
wrapper.setAttribute("id", "wrapper-" + aNode.getAttribute("id"));
|
|
|
|
}
|
|
|
|
|
2014-02-25 03:50:05 -08:00
|
|
|
if (aNode.hasAttribute("label")) {
|
2013-03-26 14:23:23 -07:00
|
|
|
wrapper.setAttribute("title", aNode.getAttribute("label"));
|
2014-02-25 03:50:05 -08:00
|
|
|
} else if (aNode.hasAttribute("title")) {
|
|
|
|
wrapper.setAttribute("title", aNode.getAttribute("title"));
|
2013-03-26 14:23:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (aNode.hasAttribute("flex")) {
|
|
|
|
wrapper.setAttribute("flex", aNode.getAttribute("flex"));
|
|
|
|
}
|
|
|
|
|
2014-03-26 10:18:06 -07:00
|
|
|
if (aPlace == "panel") {
|
|
|
|
if (aNode.classList.contains(CustomizableUI.WIDE_PANEL_CLASS)) {
|
|
|
|
wrapper.setAttribute("haswideitem", "true");
|
|
|
|
} else if (wrapper.hasAttribute("haswideitem")) {
|
|
|
|
wrapper.removeAttribute("haswideitem");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-07 10:11:55 -08:00
|
|
|
let removable = aPlace == "palette" || CustomizableUI.isWidgetRemovable(aNode);
|
|
|
|
wrapper.setAttribute("removable", removable);
|
|
|
|
|
2013-11-21 20:09:34 -08:00
|
|
|
let contextMenuAttrName = aNode.getAttribute("context") ? "context" :
|
|
|
|
aNode.getAttribute("contextmenu") ? "contextmenu" : "";
|
|
|
|
let currentContextMenu = aNode.getAttribute(contextMenuAttrName);
|
|
|
|
let contextMenuForPlace = aPlace == "panel" ?
|
|
|
|
kPanelItemContextMenu :
|
|
|
|
kPaletteItemContextMenu;
|
|
|
|
if (aPlace != "toolbar") {
|
|
|
|
wrapper.setAttribute("context", contextMenuForPlace);
|
|
|
|
}
|
2013-12-03 15:27:10 -08:00
|
|
|
// Only keep track of the menu if it is non-default.
|
|
|
|
if (currentContextMenu &&
|
|
|
|
currentContextMenu != contextMenuForPlace) {
|
2013-11-21 20:09:34 -08:00
|
|
|
aNode.setAttribute("wrapped-context", currentContextMenu);
|
|
|
|
aNode.setAttribute("wrapped-contextAttrName", contextMenuAttrName)
|
|
|
|
aNode.removeAttribute(contextMenuAttrName);
|
2013-12-03 15:27:10 -08:00
|
|
|
} else if (currentContextMenu == contextMenuForPlace) {
|
|
|
|
aNode.removeAttribute(contextMenuAttrName);
|
2013-11-21 20:09:34 -08:00
|
|
|
}
|
|
|
|
|
2014-03-11 05:29:25 -07:00
|
|
|
// Only add listeners for newly created wrappers:
|
|
|
|
if (!aIsUpdate) {
|
|
|
|
wrapper.addEventListener("mousedown", this);
|
|
|
|
wrapper.addEventListener("mouseup", this);
|
|
|
|
}
|
2013-03-22 14:28:34 -07:00
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
return wrapper;
|
|
|
|
},
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
deferredUnwrapToolbarItem: function(aWrapper) {
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
dispatchFunction(function() {
|
2013-11-25 15:26:17 -08:00
|
|
|
let item = null;
|
|
|
|
try {
|
|
|
|
item = this.unwrapToolbarItem(aWrapper);
|
|
|
|
} catch (ex) {
|
|
|
|
Cu.reportError(ex);
|
|
|
|
}
|
|
|
|
deferred.resolve(item);
|
2013-06-17 07:37:41 -07:00
|
|
|
}.bind(this));
|
|
|
|
return deferred.promise;
|
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
unwrapToolbarItem: function(aWrapper) {
|
2013-05-30 01:07:43 -07:00
|
|
|
if (aWrapper.nodeName != "toolbarpaletteitem") {
|
|
|
|
return aWrapper;
|
|
|
|
}
|
2013-03-22 14:28:34 -07:00
|
|
|
aWrapper.removeEventListener("mousedown", this);
|
|
|
|
aWrapper.removeEventListener("mouseup", this);
|
|
|
|
|
2014-02-03 02:50:23 -08:00
|
|
|
let place = aWrapper.getAttribute("place");
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
let toolbarItem = aWrapper.firstChild;
|
2013-06-20 16:13:01 -07:00
|
|
|
if (!toolbarItem) {
|
|
|
|
ERROR("no toolbarItem child for " + aWrapper.tagName + "#" + aWrapper.id);
|
2013-11-21 05:50:19 -08:00
|
|
|
aWrapper.remove();
|
|
|
|
return null;
|
2013-06-20 16:13:01 -07:00
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-12-16 20:20:13 -08:00
|
|
|
if (aWrapper.hasAttribute("itemobserves")) {
|
|
|
|
toolbarItem.setAttribute("observes", aWrapper.getAttribute("itemobserves"));
|
|
|
|
}
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
if (aWrapper.hasAttribute("itemchecked")) {
|
|
|
|
toolbarItem.checked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aWrapper.hasAttribute("itemcommand")) {
|
|
|
|
let commandID = aWrapper.getAttribute("itemcommand");
|
|
|
|
toolbarItem.setAttribute("command", commandID);
|
|
|
|
|
|
|
|
//XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing
|
|
|
|
let command = this.document.getElementById(commandID);
|
|
|
|
if (command && command.hasAttribute("disabled")) {
|
|
|
|
toolbarItem.setAttribute("disabled", command.getAttribute("disabled"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-21 20:09:34 -08:00
|
|
|
let wrappedContext = toolbarItem.getAttribute("wrapped-context");
|
|
|
|
if (wrappedContext) {
|
|
|
|
let contextAttrName = toolbarItem.getAttribute("wrapped-contextAttrName");
|
|
|
|
toolbarItem.setAttribute(contextAttrName, wrappedContext);
|
|
|
|
toolbarItem.removeAttribute("wrapped-contextAttrName");
|
|
|
|
toolbarItem.removeAttribute("wrapped-context");
|
2014-02-03 02:50:23 -08:00
|
|
|
} else if (place == "panel") {
|
|
|
|
toolbarItem.setAttribute("context", kPanelItemContextMenu);
|
2013-11-21 20:09:34 -08:00
|
|
|
}
|
|
|
|
|
2013-03-22 12:15:58 -07:00
|
|
|
if (aWrapper.parentNode) {
|
|
|
|
aWrapper.parentNode.replaceChild(toolbarItem, aWrapper);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
return toolbarItem;
|
|
|
|
},
|
|
|
|
|
2013-05-24 08:43:39 -07:00
|
|
|
_wrapToolbarItems: function() {
|
|
|
|
let window = this.window;
|
|
|
|
// Add drag-and-drop event handlers to all of the customizable areas.
|
2013-06-17 07:37:41 -07:00
|
|
|
return Task.spawn(function() {
|
|
|
|
this.areas = [];
|
|
|
|
for (let area of CustomizableUI.areas) {
|
|
|
|
let target = CustomizableUI.getCustomizeTargetForArea(area, window);
|
2014-04-13 16:42:24 -07:00
|
|
|
this._addDragHandlers(target);
|
2013-06-17 07:37:41 -07:00
|
|
|
for (let child of target.children) {
|
|
|
|
if (this.isCustomizableItem(child)) {
|
2013-11-21 20:09:34 -08:00
|
|
|
yield this.deferredWrapToolbarItem(child, CustomizableUI.getPlaceForItem(child));
|
2013-06-17 07:37:41 -07:00
|
|
|
}
|
2013-05-24 08:43:39 -07:00
|
|
|
}
|
2013-06-17 07:37:41 -07:00
|
|
|
this.areas.push(target);
|
2013-05-24 08:43:39 -07:00
|
|
|
}
|
2013-08-21 12:33:46 -07:00
|
|
|
}.bind(this)).then(null, ERROR);
|
2013-05-24 08:43:39 -07:00
|
|
|
},
|
|
|
|
|
2014-04-13 16:42:24 -07:00
|
|
|
_addDragHandlers: function(aTarget) {
|
|
|
|
aTarget.addEventListener("dragstart", this, true);
|
|
|
|
aTarget.addEventListener("dragover", this, true);
|
|
|
|
aTarget.addEventListener("dragexit", this, true);
|
|
|
|
aTarget.addEventListener("drop", this, true);
|
|
|
|
aTarget.addEventListener("dragend", this, true);
|
|
|
|
},
|
|
|
|
|
2013-08-29 06:17:28 -07:00
|
|
|
_wrapItemsInArea: function(target) {
|
|
|
|
for (let child of target.children) {
|
|
|
|
if (this.isCustomizableItem(child)) {
|
2013-11-21 20:09:34 -08:00
|
|
|
this.wrapToolbarItem(child, CustomizableUI.getPlaceForItem(child));
|
2013-06-20 16:13:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-04-13 16:42:24 -07:00
|
|
|
_removeDragHandlers: function(aTarget) {
|
|
|
|
aTarget.removeEventListener("dragstart", this, true);
|
|
|
|
aTarget.removeEventListener("dragover", this, true);
|
|
|
|
aTarget.removeEventListener("dragexit", this, true);
|
|
|
|
aTarget.removeEventListener("drop", this, true);
|
|
|
|
aTarget.removeEventListener("dragend", this, true);
|
|
|
|
},
|
|
|
|
|
2013-08-29 06:17:28 -07:00
|
|
|
_unwrapItemsInArea: function(target) {
|
|
|
|
for (let toolbarItem of target.children) {
|
|
|
|
if (this.isWrappedToolbarItem(toolbarItem)) {
|
|
|
|
this.unwrapToolbarItem(toolbarItem);
|
|
|
|
}
|
|
|
|
}
|
2013-06-20 16:13:01 -07:00
|
|
|
},
|
|
|
|
|
2013-08-29 06:17:28 -07:00
|
|
|
_unwrapToolbarItems: function() {
|
|
|
|
return Task.spawn(function() {
|
|
|
|
for (let target of this.areas) {
|
|
|
|
for (let toolbarItem of target.children) {
|
|
|
|
if (this.isWrappedToolbarItem(toolbarItem)) {
|
|
|
|
yield this.deferredUnwrapToolbarItem(toolbarItem);
|
|
|
|
}
|
2013-05-24 08:43:39 -07:00
|
|
|
}
|
2014-04-13 16:42:24 -07:00
|
|
|
this._removeDragHandlers(target);
|
2013-05-24 08:43:39 -07:00
|
|
|
}
|
2013-08-29 06:17:28 -07:00
|
|
|
}.bind(this)).then(null, ERROR);
|
2013-05-24 08:43:39 -07:00
|
|
|
},
|
|
|
|
|
2014-03-03 13:18:28 -08:00
|
|
|
_removeExtraToolbarsIfEmpty: function() {
|
|
|
|
let toolbox = this.window.gNavToolbox;
|
|
|
|
for (let child of toolbox.children) {
|
|
|
|
if (child.hasAttribute("customindex")) {
|
|
|
|
let placements = CustomizableUI.getWidgetIdsInArea(child.id);
|
|
|
|
if (!placements.length) {
|
|
|
|
CustomizableUI.removeExtraToolbar(child.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-11-14 11:16:08 -08:00
|
|
|
persistCurrentSets: function(aSetBeforePersisting) {
|
2013-03-26 14:23:23 -07:00
|
|
|
let document = this.document;
|
2013-07-16 06:36:03 -07:00
|
|
|
let toolbars = document.querySelectorAll("toolbar[customizable='true'][currentset]");
|
2013-04-29 21:25:08 -07:00
|
|
|
for (let toolbar of toolbars) {
|
2013-11-14 11:16:08 -08:00
|
|
|
if (aSetBeforePersisting) {
|
|
|
|
let set = toolbar.currentSet;
|
|
|
|
toolbar.setAttribute("currentset", set);
|
|
|
|
}
|
2013-04-29 21:25:08 -07:00
|
|
|
// Persist the currentset attribute directly on hardcoded toolbars.
|
|
|
|
document.persist(toolbar.id, "currentset");
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
reset: function() {
|
2013-10-06 04:20:49 -07:00
|
|
|
this.resetting = true;
|
2013-11-21 10:20:57 -08:00
|
|
|
// Disable the reset button temporarily while resetting:
|
|
|
|
let btn = this.document.getElementById("customization-reset-button");
|
2014-01-14 06:44:20 -08:00
|
|
|
BrowserUITelemetry.countCustomizationEvent("reset");
|
2013-11-21 10:20:57 -08:00
|
|
|
btn.disabled = true;
|
2013-06-17 07:37:41 -07:00
|
|
|
return Task.spawn(function() {
|
|
|
|
this._removePanelCustomizationPlaceholders();
|
|
|
|
yield this.depopulatePalette();
|
|
|
|
yield this._unwrapToolbarItems();
|
|
|
|
|
|
|
|
CustomizableUI.reset();
|
|
|
|
|
|
|
|
yield this._wrapToolbarItems();
|
2014-04-10 10:45:33 -07:00
|
|
|
this.populatePalette();
|
2013-06-17 07:37:41 -07:00
|
|
|
|
2013-11-14 11:16:08 -08:00
|
|
|
this.persistCurrentSets(true);
|
2013-05-24 08:43:39 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this._updateResetButton();
|
2014-02-12 09:46:26 -08:00
|
|
|
this._updateUndoResetButton();
|
2014-02-11 14:42:43 -08:00
|
|
|
this._updateEmptyPaletteNotice();
|
2013-06-17 07:37:41 -07:00
|
|
|
this._showPanelCustomizationPlaceholders();
|
2013-10-06 04:20:49 -07:00
|
|
|
this.resetting = false;
|
2014-02-26 09:55:00 -08:00
|
|
|
if (!this._wantToBeInCustomizeMode) {
|
|
|
|
this.exit();
|
|
|
|
}
|
2013-08-21 12:33:46 -07:00
|
|
|
}.bind(this)).then(null, ERROR);
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2014-02-12 09:46:26 -08:00
|
|
|
undoReset: function() {
|
|
|
|
this.resetting = true;
|
|
|
|
|
|
|
|
return Task.spawn(function() {
|
|
|
|
this._removePanelCustomizationPlaceholders();
|
|
|
|
yield this.depopulatePalette();
|
|
|
|
yield this._unwrapToolbarItems();
|
|
|
|
|
|
|
|
CustomizableUI.undoReset();
|
|
|
|
|
|
|
|
yield this._wrapToolbarItems();
|
2014-04-10 10:45:33 -07:00
|
|
|
this.populatePalette();
|
2014-02-12 09:46:26 -08:00
|
|
|
|
|
|
|
this.persistCurrentSets(true);
|
|
|
|
|
|
|
|
this._updateResetButton();
|
|
|
|
this._updateUndoResetButton();
|
|
|
|
this._updateEmptyPaletteNotice();
|
|
|
|
this.resetting = false;
|
|
|
|
}.bind(this)).then(null, ERROR);
|
|
|
|
},
|
|
|
|
|
2013-07-10 05:05:35 -07:00
|
|
|
_onToolbarVisibilityChange: function(aEvent) {
|
|
|
|
let toolbar = aEvent.target;
|
2014-02-06 10:40:19 -08:00
|
|
|
if (aEvent.detail.visible && toolbar.getAttribute("customizable") == "true") {
|
2013-07-10 05:05:35 -07:00
|
|
|
toolbar.setAttribute("customizing", "true");
|
|
|
|
} else {
|
|
|
|
toolbar.removeAttribute("customizing");
|
|
|
|
}
|
2014-02-07 22:25:25 -08:00
|
|
|
this._onUIChange();
|
2013-07-10 05:05:35 -07:00
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
onWidgetMoved: function(aWidgetId, aArea, aOldPosition, aNewPosition) {
|
2013-05-17 12:29:05 -07:00
|
|
|
this._onUIChange();
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
onWidgetAdded: function(aWidgetId, aArea, aPosition) {
|
2013-05-17 12:29:05 -07:00
|
|
|
this._onUIChange();
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
onWidgetRemoved: function(aWidgetId, aArea) {
|
2013-05-17 12:29:05 -07:00
|
|
|
this._onUIChange();
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2013-08-29 06:17:28 -07:00
|
|
|
onWidgetBeforeDOMChange: function(aNodeToChange, aSecondaryNode, aContainer) {
|
2013-10-06 04:20:49 -07:00
|
|
|
if (aContainer.ownerDocument.defaultView != this.window || this.resetting) {
|
2013-08-29 06:17:28 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (aContainer.id == CustomizableUI.AREA_PANEL) {
|
|
|
|
this._removePanelCustomizationPlaceholders();
|
|
|
|
}
|
2013-09-24 02:39:01 -07:00
|
|
|
// If we get called for widgets that aren't in the window yet, they might not have
|
|
|
|
// a parentNode at all.
|
|
|
|
if (aNodeToChange.parentNode) {
|
|
|
|
this.unwrapToolbarItem(aNodeToChange.parentNode);
|
|
|
|
}
|
2013-08-29 06:17:28 -07:00
|
|
|
if (aSecondaryNode) {
|
|
|
|
this.unwrapToolbarItem(aSecondaryNode.parentNode);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2013-08-29 06:17:28 -07:00
|
|
|
onWidgetAfterDOMChange: function(aNodeToChange, aSecondaryNode, aContainer) {
|
2013-10-06 04:20:49 -07:00
|
|
|
if (aContainer.ownerDocument.defaultView != this.window || this.resetting) {
|
2013-08-29 06:17:28 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// If the node is still attached to the container, wrap it again:
|
|
|
|
if (aNodeToChange.parentNode) {
|
2013-11-21 20:09:34 -08:00
|
|
|
let place = CustomizableUI.getPlaceForItem(aNodeToChange);
|
2013-09-19 08:29:57 -07:00
|
|
|
this.wrapToolbarItem(aNodeToChange, place);
|
2013-08-29 06:17:28 -07:00
|
|
|
if (aSecondaryNode) {
|
2013-09-19 08:29:57 -07:00
|
|
|
this.wrapToolbarItem(aSecondaryNode, place);
|
2013-08-29 06:17:28 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If not, it got removed.
|
|
|
|
|
|
|
|
// If an API-based widget is removed while customizing, append it to the palette.
|
|
|
|
// The _applyDrop code itself will take care of positioning it correctly, if
|
|
|
|
// applicable. We need the code to be here so removing widgets using CustomizableUI's
|
|
|
|
// API also does the right thing (and adds it to the palette)
|
|
|
|
let widgetId = aNodeToChange.id;
|
|
|
|
let widget = CustomizableUI.getWidget(widgetId);
|
|
|
|
if (widget.provider == CustomizableUI.PROVIDER_API) {
|
|
|
|
let paletteItem = this.makePaletteItem(widget, "palette");
|
|
|
|
this.visiblePalette.appendChild(paletteItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (aContainer.id == CustomizableUI.AREA_PANEL) {
|
|
|
|
this._showPanelCustomizationPlaceholders();
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2013-10-23 04:03:55 -07:00
|
|
|
onWidgetDestroyed: function(aWidgetId) {
|
|
|
|
let wrapper = this.document.getElementById("wrapper-" + aWidgetId);
|
|
|
|
if (wrapper) {
|
|
|
|
let wasInPanel = wrapper.parentNode == this.panelUIContents;
|
|
|
|
wrapper.remove();
|
|
|
|
if (wasInPanel) {
|
|
|
|
this._showPanelCustomizationPlaceholders();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onWidgetAfterCreation: function(aWidgetId, aArea) {
|
|
|
|
// If the node was added to an area, we would have gotten an onWidgetAdded notification,
|
|
|
|
// plus associated DOM change notifications, so only do stuff for the palette:
|
|
|
|
if (!aArea) {
|
|
|
|
let widgetNode = this.document.getElementById(aWidgetId);
|
|
|
|
if (widgetNode) {
|
|
|
|
this.wrapToolbarItem(widgetNode, "palette");
|
|
|
|
} else {
|
|
|
|
let widget = CustomizableUI.getWidget(aWidgetId);
|
|
|
|
this.visiblePalette.appendChild(this.makePaletteItem(widget, "palette"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-04-13 16:42:24 -07:00
|
|
|
onAreaNodeRegistered: function(aArea, aContainer) {
|
|
|
|
if (aContainer.ownerDocument == this.document) {
|
|
|
|
this._wrapItemsInArea(aContainer);
|
|
|
|
this._addDragHandlers(aContainer);
|
|
|
|
DragPositionManager.add(this.window, aArea, aContainer);
|
|
|
|
this.areas.push(aContainer);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onAreaNodeUnregistered: function(aArea, aContainer, aReason) {
|
|
|
|
if (aContainer.ownerDocument == this.document && aReason == CustomizableUI.REASON_AREA_UNREGISTERED) {
|
|
|
|
this._unwrapItemsInArea(aContainer);
|
|
|
|
this._removeDragHandlers(aContainer);
|
|
|
|
DragPositionManager.remove(this.window, aArea, aContainer);
|
|
|
|
let index = this.areas.indexOf(aContainer);
|
|
|
|
this.areas.splice(index, 1);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-05-17 12:29:05 -07:00
|
|
|
_onUIChange: function() {
|
|
|
|
this._changed = true;
|
2014-02-11 14:42:43 -08:00
|
|
|
if (!this.resetting) {
|
|
|
|
this._updateResetButton();
|
2014-02-12 09:46:26 -08:00
|
|
|
this._updateUndoResetButton();
|
2014-02-11 14:42:43 -08:00
|
|
|
this._updateEmptyPaletteNotice();
|
|
|
|
}
|
2014-02-19 11:44:02 -08:00
|
|
|
CustomizableUI.dispatchToolboxEvent("customizationchange");
|
2013-05-17 12:29:05 -07:00
|
|
|
},
|
|
|
|
|
2014-01-21 11:04:24 -08:00
|
|
|
_updateEmptyPaletteNotice: function() {
|
|
|
|
let paletteItems = this.visiblePalette.getElementsByTagName("toolbarpaletteitem");
|
|
|
|
this.paletteEmptyNotice.hidden = !!paletteItems.length;
|
|
|
|
},
|
|
|
|
|
2013-06-06 12:55:19 -07:00
|
|
|
_updateResetButton: function() {
|
|
|
|
let btn = this.document.getElementById("customization-reset-button");
|
|
|
|
btn.disabled = CustomizableUI.inDefaultState;
|
|
|
|
},
|
|
|
|
|
2014-02-12 09:46:26 -08:00
|
|
|
_updateUndoResetButton: function() {
|
2014-02-14 10:51:33 -08:00
|
|
|
let undoResetButton = this.document.getElementById("customization-undo-reset-button");
|
|
|
|
undoResetButton.hidden = !CustomizableUI.canUndoReset;
|
2014-02-12 09:46:26 -08:00
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
handleEvent: function(aEvent) {
|
|
|
|
switch(aEvent.type) {
|
2013-07-10 05:05:35 -07:00
|
|
|
case "toolbarvisibilitychange":
|
|
|
|
this._onToolbarVisibilityChange(aEvent);
|
|
|
|
break;
|
2013-03-26 14:23:23 -07:00
|
|
|
case "dragstart":
|
|
|
|
this._onDragStart(aEvent);
|
|
|
|
break;
|
|
|
|
case "dragover":
|
|
|
|
this._onDragOver(aEvent);
|
|
|
|
break;
|
|
|
|
case "drop":
|
|
|
|
this._onDragDrop(aEvent);
|
|
|
|
break;
|
2013-03-22 12:26:26 -07:00
|
|
|
case "dragexit":
|
|
|
|
this._onDragExit(aEvent);
|
|
|
|
break;
|
2013-05-16 21:25:40 -07:00
|
|
|
case "dragend":
|
|
|
|
this._onDragEnd(aEvent);
|
|
|
|
break;
|
2014-04-24 07:37:54 -07:00
|
|
|
case "command":
|
|
|
|
if (aEvent.originalTarget == this.window.PanelUI.menuButton) {
|
2013-09-25 06:35:15 -07:00
|
|
|
this.exit();
|
|
|
|
aEvent.preventDefault();
|
|
|
|
}
|
2014-04-24 07:37:54 -07:00
|
|
|
break;
|
|
|
|
case "mousedown":
|
2013-03-22 14:28:34 -07:00
|
|
|
this._onMouseDown(aEvent);
|
|
|
|
break;
|
|
|
|
case "mouseup":
|
|
|
|
this._onMouseUp(aEvent);
|
|
|
|
break;
|
2013-03-22 12:15:58 -07:00
|
|
|
case "keypress":
|
2013-04-10 13:24:45 -07:00
|
|
|
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
|
2013-03-22 12:15:58 -07:00
|
|
|
this.exit();
|
|
|
|
}
|
|
|
|
break;
|
2014-01-29 09:19:23 -08:00
|
|
|
#ifdef CAN_DRAW_IN_TITLEBAR
|
|
|
|
case "unload":
|
|
|
|
this.uninit();
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
#ifdef CAN_DRAW_IN_TITLEBAR
|
|
|
|
observe: function(aSubject, aTopic, aData) {
|
|
|
|
switch (aTopic) {
|
|
|
|
case "nsPref:changed":
|
2014-02-14 10:51:33 -08:00
|
|
|
this._updateResetButton();
|
2014-01-29 09:19:23 -08:00
|
|
|
this._updateTitlebarButton();
|
2014-02-14 10:51:33 -08:00
|
|
|
this._updateUndoResetButton();
|
2014-01-29 09:19:23 -08:00
|
|
|
break;
|
2013-03-26 14:23:23 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-01-29 09:19:23 -08:00
|
|
|
_updateTitlebarButton: function() {
|
|
|
|
let drawInTitlebar = true;
|
|
|
|
try {
|
|
|
|
drawInTitlebar = Services.prefs.getBoolPref(kDrawInTitlebarPref);
|
|
|
|
} catch (ex) { }
|
|
|
|
let button = this.document.getElementById("customization-titlebar-visibility-button");
|
|
|
|
// Drawing in the titlebar means 'hiding' the titlebar:
|
|
|
|
if (drawInTitlebar) {
|
|
|
|
button.removeAttribute("checked");
|
|
|
|
} else {
|
|
|
|
button.setAttribute("checked", "true");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
toggleTitlebar: function(aShouldShowTitlebar) {
|
|
|
|
// Drawing in the titlebar means not showing the titlebar, hence the negation:
|
|
|
|
Services.prefs.setBoolPref(kDrawInTitlebarPref, !aShouldShowTitlebar);
|
|
|
|
},
|
|
|
|
#endif
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
_onDragStart: function(aEvent) {
|
|
|
|
__dumpDragData(aEvent);
|
|
|
|
let item = aEvent.target;
|
|
|
|
while (item && item.localName != "toolbarpaletteitem") {
|
2013-11-21 07:38:22 -08:00
|
|
|
if (item.localName == "toolbar") {
|
2013-03-26 14:23:23 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
item = item.parentNode;
|
|
|
|
}
|
|
|
|
|
2014-02-07 10:11:55 -08:00
|
|
|
let draggedItem = item.firstChild;
|
|
|
|
let placeForItem = CustomizableUI.getPlaceForItem(item);
|
|
|
|
let isRemovable = placeForItem == "palette" ||
|
|
|
|
CustomizableUI.isWidgetRemovable(draggedItem);
|
|
|
|
if (item.classList.contains(kPlaceholderClass) || !isRemovable) {
|
2013-11-21 07:38:22 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
let dt = aEvent.dataTransfer;
|
|
|
|
let documentId = aEvent.target.ownerDocument.documentElement.id;
|
2014-02-07 10:11:55 -08:00
|
|
|
let isInToolbar = placeForItem == "toolbar";
|
2013-05-29 13:41:44 -07:00
|
|
|
|
2013-08-26 15:23:18 -07:00
|
|
|
dt.mozSetDataAt(kDragDataTypePrefix + documentId, draggedItem.id, 0);
|
2013-03-26 14:23:23 -07:00
|
|
|
dt.effectAllowed = "move";
|
2013-06-03 15:35:30 -07:00
|
|
|
|
2013-10-31 10:02:49 -07:00
|
|
|
let itemRect = draggedItem.getBoundingClientRect();
|
|
|
|
let itemCenter = {x: itemRect.left + itemRect.width / 2,
|
|
|
|
y: itemRect.top + itemRect.height / 2};
|
|
|
|
this._dragOffset = {x: aEvent.clientX - itemCenter.x,
|
|
|
|
y: aEvent.clientY - itemCenter.y};
|
|
|
|
|
2014-02-03 08:38:00 -08:00
|
|
|
gDraggingInToolbars = new Set();
|
|
|
|
|
2013-06-03 15:35:30 -07:00
|
|
|
// Hack needed so that the dragimage will still show the
|
|
|
|
// item as it appeared before it was hidden.
|
2013-11-13 05:20:32 -08:00
|
|
|
this._initializeDragAfterMove = function() {
|
2013-07-23 08:11:09 -07:00
|
|
|
// For automated tests, we sometimes start exiting customization mode
|
|
|
|
// before this fires, which leaves us with placeholders inserted after
|
|
|
|
// we've exited. So we need to check that we are indeed customizing.
|
|
|
|
if (this._customizing && !this._transitioning) {
|
|
|
|
item.hidden = true;
|
|
|
|
this._showPanelCustomizationPlaceholders();
|
2013-11-13 05:20:32 -08:00
|
|
|
DragPositionManager.start(this.window);
|
2014-02-03 08:38:00 -08:00
|
|
|
if (item.nextSibling) {
|
|
|
|
this._setDragActive(item.nextSibling, "before", draggedItem.id, isInToolbar);
|
2014-02-12 14:55:28 -08:00
|
|
|
this._dragOverItem = item.nextSibling;
|
2014-02-03 08:38:00 -08:00
|
|
|
} else if (isInToolbar && item.previousSibling) {
|
|
|
|
this._setDragActive(item.previousSibling, "after", draggedItem.id, isInToolbar);
|
2014-02-12 14:55:28 -08:00
|
|
|
this._dragOverItem = item.previousSibling;
|
2014-01-15 15:44:48 -08:00
|
|
|
}
|
2013-07-23 08:11:09 -07:00
|
|
|
}
|
2013-11-13 05:20:32 -08:00
|
|
|
this._initializeDragAfterMove = null;
|
|
|
|
this.window.clearTimeout(this._dragInitializeTimeout);
|
|
|
|
}.bind(this);
|
|
|
|
this._dragInitializeTimeout = this.window.setTimeout(this._initializeDragAfterMove, 0);
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
_onDragOver: function(aEvent) {
|
2013-09-10 11:37:23 -07:00
|
|
|
if (this._isUnwantedDragDrop(aEvent)) {
|
|
|
|
return;
|
|
|
|
}
|
2013-11-13 05:20:32 -08:00
|
|
|
if (this._initializeDragAfterMove) {
|
|
|
|
this._initializeDragAfterMove();
|
|
|
|
}
|
2013-09-10 11:37:23 -07:00
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
__dumpDragData(aEvent);
|
|
|
|
|
2013-03-22 12:26:26 -07:00
|
|
|
let document = aEvent.target.ownerDocument;
|
|
|
|
let documentId = document.documentElement.id;
|
2013-05-29 13:41:44 -07:00
|
|
|
if (!aEvent.dataTransfer.mozTypesAt(0)) {
|
2013-03-26 14:23:23 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-26 15:23:18 -07:00
|
|
|
let draggedItemId =
|
2013-05-29 13:41:44 -07:00
|
|
|
aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
|
2013-03-22 12:26:26 -07:00
|
|
|
let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
|
2013-05-30 01:07:43 -07:00
|
|
|
let targetArea = this._getCustomizableParent(aEvent.currentTarget);
|
2013-03-22 12:26:26 -07:00
|
|
|
let originArea = this._getCustomizableParent(draggedWrapper);
|
|
|
|
|
|
|
|
// Do nothing if the target or origin are not customizable.
|
|
|
|
if (!targetArea || !originArea) {
|
2013-05-21 21:49:37 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-03 15:35:30 -07:00
|
|
|
// Do nothing if the widget is not allowed to be removed.
|
2013-05-21 21:49:37 -07:00
|
|
|
if (targetArea.id == kPaletteId &&
|
|
|
|
!CustomizableUI.isWidgetRemovable(draggedItemId)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do nothing if the widget is not allowed to move to the target area.
|
|
|
|
if (targetArea.id != kPaletteId &&
|
|
|
|
!CustomizableUI.canWidgetMoveToArea(draggedItemId, targetArea.id)) {
|
2013-03-22 12:26:26 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
let targetIsToolbar = CustomizableUI.getAreaType(targetArea.id) == "toolbar";
|
|
|
|
let targetNode = this._getDragOverNode(aEvent, targetArea, targetIsToolbar, draggedItemId);
|
2013-05-30 01:07:43 -07:00
|
|
|
|
2013-03-22 12:26:26 -07:00
|
|
|
// We need to determine the place that the widget is being dropped in
|
|
|
|
// the target.
|
2013-10-15 07:32:25 -07:00
|
|
|
let dragOverItem, dragValue;
|
2013-05-29 13:41:44 -07:00
|
|
|
if (targetNode == targetArea.customizationTarget) {
|
2013-10-16 11:52:00 -07:00
|
|
|
// We'll assume if the user is dragging directly over the target, that
|
|
|
|
// they're attempting to append a child to that target.
|
2014-03-05 12:03:00 -08:00
|
|
|
dragOverItem = (targetIsToolbar ? this._findVisiblePreviousSiblingNode(targetNode.lastChild) :
|
|
|
|
targetNode.lastChild) || targetNode;
|
2013-10-15 09:20:04 -07:00
|
|
|
dragValue = "after";
|
2013-05-29 13:41:44 -07:00
|
|
|
} else {
|
2013-10-31 10:02:49 -07:00
|
|
|
let targetParent = targetNode.parentNode;
|
2013-05-29 13:41:44 -07:00
|
|
|
let position = Array.indexOf(targetParent.children, targetNode);
|
2013-09-11 08:38:18 -07:00
|
|
|
if (position == -1) {
|
2014-03-05 12:03:00 -08:00
|
|
|
dragOverItem = targetIsToolbar ? this._findVisiblePreviousSiblingNode(targetNode.lastChild) :
|
|
|
|
targetParent.lastChild;
|
2013-10-15 09:20:04 -07:00
|
|
|
dragValue = "after";
|
2013-09-11 08:38:18 -07:00
|
|
|
} else {
|
|
|
|
dragOverItem = targetParent.children[position];
|
2013-11-13 05:20:32 -08:00
|
|
|
if (!targetIsToolbar) {
|
|
|
|
dragValue = "before";
|
2013-10-15 07:32:25 -07:00
|
|
|
} else {
|
2014-03-05 12:03:00 -08:00
|
|
|
dragOverItem = this._findVisiblePreviousSiblingNode(targetParent.children[position]);
|
2013-11-13 05:20:32 -08:00
|
|
|
// Check if the aDraggedItem is hovered past the first half of dragOverItem
|
|
|
|
let window = dragOverItem.ownerDocument.defaultView;
|
|
|
|
let direction = window.getComputedStyle(dragOverItem, null).direction;
|
|
|
|
let itemRect = dragOverItem.getBoundingClientRect();
|
|
|
|
let dropTargetCenter = itemRect.left + (itemRect.width / 2);
|
2013-10-15 07:32:25 -07:00
|
|
|
let existingDir = dragOverItem.getAttribute("dragover");
|
|
|
|
if ((existingDir == "before") == (direction == "ltr")) {
|
|
|
|
dropTargetCenter += (parseInt(dragOverItem.style.borderLeftWidth) || 0) / 2;
|
|
|
|
} else {
|
|
|
|
dropTargetCenter -= (parseInt(dragOverItem.style.borderRightWidth) || 0) / 2;
|
|
|
|
}
|
|
|
|
let before = direction == "ltr" ? aEvent.clientX < dropTargetCenter : aEvent.clientX > dropTargetCenter;
|
|
|
|
dragValue = before ? "before" : "after";
|
2013-09-11 08:38:18 -07:00
|
|
|
}
|
|
|
|
}
|
2013-05-29 13:41:44 -07:00
|
|
|
}
|
2013-03-22 12:26:26 -07:00
|
|
|
|
|
|
|
if (this._dragOverItem && dragOverItem != this._dragOverItem) {
|
2013-11-13 05:20:32 -08:00
|
|
|
this._cancelDragActive(this._dragOverItem, dragOverItem);
|
2013-03-22 12:26:26 -07:00
|
|
|
}
|
|
|
|
|
2013-10-15 07:32:25 -07:00
|
|
|
if (dragOverItem != this._dragOverItem || dragValue != dragOverItem.getAttribute("dragover")) {
|
2013-10-16 11:52:00 -07:00
|
|
|
if (dragOverItem != targetArea.customizationTarget) {
|
2013-11-13 05:20:32 -08:00
|
|
|
this._setDragActive(dragOverItem, dragValue, draggedItemId, targetIsToolbar);
|
2014-03-09 10:57:13 -07:00
|
|
|
} else if (targetIsToolbar) {
|
|
|
|
this._updateToolbarCustomizationOutline(this.window, targetArea);
|
2013-10-16 11:52:00 -07:00
|
|
|
}
|
2013-06-03 15:35:30 -07:00
|
|
|
this._dragOverItem = dragOverItem;
|
2013-03-22 12:26:26 -07:00
|
|
|
}
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
aEvent.preventDefault();
|
|
|
|
aEvent.stopPropagation();
|
|
|
|
},
|
|
|
|
|
|
|
|
_onDragDrop: function(aEvent) {
|
2013-09-10 11:37:23 -07:00
|
|
|
if (this._isUnwantedDragDrop(aEvent)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
__dumpDragData(aEvent);
|
2013-11-13 05:20:32 -08:00
|
|
|
this._initializeDragAfterMove = null;
|
|
|
|
this.window.clearTimeout(this._dragInitializeTimeout);
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-20 16:13:01 -07:00
|
|
|
let targetArea = this._getCustomizableParent(aEvent.currentTarget);
|
2013-03-26 14:23:23 -07:00
|
|
|
let document = aEvent.target.ownerDocument;
|
|
|
|
let documentId = document.documentElement.id;
|
2013-08-26 15:23:18 -07:00
|
|
|
let draggedItemId =
|
2013-05-29 13:41:44 -07:00
|
|
|
aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
|
2013-03-26 14:23:23 -07:00
|
|
|
let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
|
|
|
|
let originArea = this._getCustomizableParent(draggedWrapper);
|
2013-11-13 05:20:32 -08:00
|
|
|
if (this._dragSizeMap) {
|
|
|
|
this._dragSizeMap.clear();
|
2013-08-29 23:55:39 -07:00
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
// Do nothing if the target area or origin area are not customizable.
|
|
|
|
if (!targetArea || !originArea) {
|
|
|
|
return;
|
|
|
|
}
|
2013-10-15 07:32:25 -07:00
|
|
|
let targetNode = this._dragOverItem;
|
|
|
|
let dropDir = targetNode.getAttribute("dragover");
|
|
|
|
// Need to insert *after* this node if we promised the user that:
|
|
|
|
if (targetNode != targetArea && dropDir == "after") {
|
|
|
|
if (targetNode.nextSibling) {
|
|
|
|
targetNode = targetNode.nextSibling;
|
|
|
|
} else {
|
|
|
|
targetNode = targetArea;
|
|
|
|
}
|
|
|
|
}
|
2013-09-11 08:38:18 -07:00
|
|
|
// If the target node is a placeholder, get its sibling as the real target.
|
|
|
|
while (targetNode.classList.contains(kPlaceholderClass) && targetNode.nextSibling) {
|
|
|
|
targetNode = targetNode.nextSibling;
|
|
|
|
}
|
2013-06-20 16:13:01 -07:00
|
|
|
if (targetNode.tagName == "toolbarpaletteitem") {
|
|
|
|
targetNode = targetNode.firstChild;
|
|
|
|
}
|
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
this._cancelDragActive(this._dragOverItem, null, true);
|
2013-06-20 16:13:01 -07:00
|
|
|
this._removePanelCustomizationPlaceholders();
|
|
|
|
|
|
|
|
try {
|
|
|
|
this._applyDrop(aEvent, targetArea, originArea, draggedItemId, targetNode);
|
|
|
|
} catch (ex) {
|
|
|
|
ERROR(ex, ex.stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._showPanelCustomizationPlaceholders();
|
|
|
|
},
|
|
|
|
|
|
|
|
_applyDrop: function(aEvent, aTargetArea, aOriginArea, aDraggedItemId, aTargetNode) {
|
|
|
|
let document = aEvent.target.ownerDocument;
|
|
|
|
let draggedItem = document.getElementById(aDraggedItemId);
|
|
|
|
draggedItem.hidden = false;
|
|
|
|
draggedItem.removeAttribute("mousedown");
|
2013-05-30 01:07:43 -07:00
|
|
|
|
2013-04-14 15:52:24 -07:00
|
|
|
// Do nothing if the target was dropped onto itself (ie, no change in area
|
|
|
|
// or position).
|
2013-06-20 16:13:01 -07:00
|
|
|
if (draggedItem == aTargetNode) {
|
2013-04-14 15:52:24 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-29 06:17:28 -07:00
|
|
|
// Is the target area the customization palette?
|
2013-06-20 16:13:01 -07:00
|
|
|
if (aTargetArea.id == kPaletteId) {
|
2013-08-29 06:17:28 -07:00
|
|
|
// Did we drag from outside the palette?
|
2013-06-20 16:13:01 -07:00
|
|
|
if (aOriginArea.id !== kPaletteId) {
|
|
|
|
if (!CustomizableUI.isWidgetRemovable(aDraggedItemId)) {
|
2013-05-21 21:49:37 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-20 16:13:01 -07:00
|
|
|
CustomizableUI.removeWidgetFromArea(aDraggedItemId);
|
2014-01-14 06:44:20 -08:00
|
|
|
BrowserUITelemetry.countCustomizationEvent("remove");
|
2014-01-09 04:42:50 -08:00
|
|
|
// Special widgets are removed outright, we can return here:
|
|
|
|
if (CustomizableUI.isSpecialWidget(aDraggedItemId)) {
|
|
|
|
return;
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
}
|
2013-08-29 06:17:28 -07:00
|
|
|
draggedItem = draggedItem.parentNode;
|
2013-03-22 12:15:58 -07:00
|
|
|
|
2013-06-20 16:13:01 -07:00
|
|
|
// If the target node is the palette itself, just append
|
|
|
|
if (aTargetNode == this.visiblePalette) {
|
|
|
|
this.visiblePalette.appendChild(draggedItem);
|
2013-03-22 12:15:58 -07:00
|
|
|
} else {
|
2013-08-29 06:17:28 -07:00
|
|
|
// The items in the palette are wrapped, so we need the target node's parent here:
|
|
|
|
this.visiblePalette.insertBefore(draggedItem, aTargetNode.parentNode);
|
2013-03-22 12:15:58 -07:00
|
|
|
}
|
2014-03-04 08:12:41 -08:00
|
|
|
if (aOriginArea.id !== kPaletteId) {
|
|
|
|
// The dragend event already fires when the item moves within the palette.
|
|
|
|
this._onDragEnd(aEvent);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-20 16:13:01 -07:00
|
|
|
if (!CustomizableUI.canWidgetMoveToArea(aDraggedItemId, aTargetArea.id)) {
|
2013-05-21 21:49:37 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-02 05:52:56 -07:00
|
|
|
// Skipintoolbarset items won't really be moved:
|
|
|
|
if (draggedItem.getAttribute("skipintoolbarset") == "true") {
|
|
|
|
// These items should never leave their area:
|
|
|
|
if (aTargetArea != aOriginArea) {
|
|
|
|
return;
|
|
|
|
}
|
2013-10-23 04:03:55 -07:00
|
|
|
let place = draggedItem.parentNode.getAttribute("place");
|
2013-10-02 05:52:56 -07:00
|
|
|
this.unwrapToolbarItem(draggedItem.parentNode);
|
|
|
|
if (aTargetNode == aTargetArea.customizationTarget) {
|
|
|
|
aTargetArea.customizationTarget.appendChild(draggedItem);
|
|
|
|
} else {
|
|
|
|
this.unwrapToolbarItem(aTargetNode.parentNode);
|
|
|
|
aTargetArea.customizationTarget.insertBefore(draggedItem, aTargetNode);
|
2013-10-23 04:03:55 -07:00
|
|
|
this.wrapToolbarItem(aTargetNode, place);
|
2013-10-02 05:52:56 -07:00
|
|
|
}
|
2013-10-23 04:03:55 -07:00
|
|
|
this.wrapToolbarItem(draggedItem, place);
|
2014-01-14 06:44:20 -08:00
|
|
|
BrowserUITelemetry.countCustomizationEvent("move");
|
2013-10-02 05:52:56 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-11 12:50:45 -07:00
|
|
|
// Is the target the customization area itself? If so, we just add the
|
|
|
|
// widget to the end of the area.
|
2013-06-20 16:13:01 -07:00
|
|
|
if (aTargetNode == aTargetArea.customizationTarget) {
|
|
|
|
CustomizableUI.addWidgetToArea(aDraggedItemId, aTargetArea.id);
|
2014-01-14 06:44:20 -08:00
|
|
|
// For the purposes of BrowserUITelemetry, we consider both moving a widget
|
|
|
|
// within the same area, and adding a widget from one area to another area
|
|
|
|
// as a "move". An "add" is only when we move an item from the palette into
|
|
|
|
// an area.
|
|
|
|
let custEventType = aOriginArea.id == kPaletteId ? "add" : "move";
|
|
|
|
BrowserUITelemetry.countCustomizationEvent(custEventType);
|
2014-03-04 08:12:41 -08:00
|
|
|
this._onDragEnd(aEvent);
|
2013-04-11 12:50:45 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-05 06:58:31 -07:00
|
|
|
// We need to determine the place that the widget is being dropped in
|
|
|
|
// the target.
|
2013-06-03 15:35:30 -07:00
|
|
|
let placement;
|
2013-10-02 05:52:56 -07:00
|
|
|
let itemForPlacement = aTargetNode;
|
|
|
|
// Skip the skipintoolbarset items when determining the place of the item:
|
|
|
|
while (itemForPlacement && itemForPlacement.getAttribute("skipintoolbarset") == "true" &&
|
|
|
|
itemForPlacement.parentNode &&
|
|
|
|
itemForPlacement.parentNode.nodeName == "toolbarpaletteitem") {
|
|
|
|
itemForPlacement = itemForPlacement.parentNode.nextSibling;
|
|
|
|
if (itemForPlacement && itemForPlacement.nodeName == "toolbarpaletteitem") {
|
|
|
|
itemForPlacement = itemForPlacement.firstChild;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (itemForPlacement && !itemForPlacement.classList.contains(kPlaceholderClass)) {
|
|
|
|
let targetNodeId = (itemForPlacement.nodeName == "toolbarpaletteitem") ?
|
|
|
|
itemForPlacement.firstChild && itemForPlacement.firstChild.id :
|
|
|
|
itemForPlacement.id;
|
2013-06-03 15:35:30 -07:00
|
|
|
placement = CustomizableUI.getPlacementOfWidget(targetNodeId);
|
|
|
|
}
|
2013-04-05 06:58:31 -07:00
|
|
|
if (!placement) {
|
2013-10-02 05:52:56 -07:00
|
|
|
LOG("Could not get a position for " + aTargetNode.nodeName + "#" + aTargetNode.id + "." + aTargetNode.className);
|
2013-04-05 06:58:31 -07:00
|
|
|
}
|
2013-07-24 07:58:15 -07:00
|
|
|
let position = placement ? placement.position : null;
|
2013-04-05 06:58:31 -07:00
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
// Is the target area the same as the origin? Since we've already handled
|
|
|
|
// the possibility that the target is the customization palette, we know
|
|
|
|
// that the widget is moving within a customizable area.
|
2013-06-20 16:13:01 -07:00
|
|
|
if (aTargetArea == aOriginArea) {
|
|
|
|
CustomizableUI.moveWidgetWithinArea(aDraggedItemId, position);
|
2013-10-02 05:52:56 -07:00
|
|
|
} else {
|
|
|
|
CustomizableUI.addWidgetToArea(aDraggedItemId, aTargetArea.id, position);
|
|
|
|
}
|
2014-01-14 06:44:20 -08:00
|
|
|
|
2014-03-04 08:12:41 -08:00
|
|
|
this._onDragEnd(aEvent);
|
|
|
|
|
2014-01-14 06:44:20 -08:00
|
|
|
// For BrowserUITelemetry, an "add" is only when we move an item from the palette
|
|
|
|
// into an area. Otherwise, it's a move.
|
|
|
|
let custEventType = aOriginArea.id == kPaletteId ? "add" : "move";
|
|
|
|
BrowserUITelemetry.countCustomizationEvent(custEventType);
|
|
|
|
|
2013-10-02 05:52:56 -07:00
|
|
|
// If we dropped onto a skipintoolbarset item, manually correct the drop location:
|
|
|
|
if (aTargetNode != itemForPlacement) {
|
|
|
|
let draggedWrapper = draggedItem.parentNode;
|
|
|
|
let container = draggedWrapper.parentNode;
|
|
|
|
container.insertBefore(draggedWrapper, aTargetNode.parentNode);
|
2013-03-26 14:23:23 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-03-22 12:26:26 -07:00
|
|
|
_onDragExit: function(aEvent) {
|
2013-09-10 11:37:23 -07:00
|
|
|
if (this._isUnwantedDragDrop(aEvent)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-05-16 21:25:40 -07:00
|
|
|
__dumpDragData(aEvent);
|
2013-09-10 11:37:23 -07:00
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
// When leaving customization areas, cancel the drag on the last dragover item
|
|
|
|
// We've attached the listener to areas, so aEvent.currentTarget will be the area.
|
|
|
|
// We don't care about dragexit events fired on descendants of the area,
|
|
|
|
// so we check that the event's target is the same as the area to which the listener
|
|
|
|
// was attached.
|
|
|
|
if (this._dragOverItem && aEvent.target == aEvent.currentTarget) {
|
|
|
|
this._cancelDragActive(this._dragOverItem);
|
|
|
|
this._dragOverItem = null;
|
2013-03-22 12:26:26 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-03-04 08:12:41 -08:00
|
|
|
/**
|
|
|
|
* To workaround bug 460801 we manually forward the drop event here when dragend wouldn't be fired.
|
|
|
|
*/
|
2013-05-16 21:25:40 -07:00
|
|
|
_onDragEnd: function(aEvent) {
|
2013-09-10 11:37:23 -07:00
|
|
|
if (this._isUnwantedDragDrop(aEvent)) {
|
|
|
|
return;
|
|
|
|
}
|
2013-11-13 05:20:32 -08:00
|
|
|
this._initializeDragAfterMove = null;
|
|
|
|
this.window.clearTimeout(this._dragInitializeTimeout);
|
2014-03-04 08:12:41 -08:00
|
|
|
__dumpDragData(aEvent, "_onDragEnd");
|
2013-09-10 11:37:23 -07:00
|
|
|
|
2013-05-16 21:25:40 -07:00
|
|
|
let document = aEvent.target.ownerDocument;
|
|
|
|
document.documentElement.removeAttribute("customizing-movingItem");
|
|
|
|
|
|
|
|
let documentId = document.documentElement.id;
|
2013-05-29 13:41:44 -07:00
|
|
|
if (!aEvent.dataTransfer.mozTypesAt(0)) {
|
2013-05-16 21:25:40 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-26 15:23:18 -07:00
|
|
|
let draggedItemId =
|
2013-05-29 13:41:44 -07:00
|
|
|
aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
|
2013-05-16 21:25:40 -07:00
|
|
|
|
2013-05-29 13:41:44 -07:00
|
|
|
let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
|
2013-06-03 15:35:30 -07:00
|
|
|
draggedWrapper.hidden = false;
|
2013-05-16 21:25:40 -07:00
|
|
|
draggedWrapper.removeAttribute("mousedown");
|
2014-01-30 08:30:52 -08:00
|
|
|
if (this._dragOverItem) {
|
|
|
|
this._cancelDragActive(this._dragOverItem);
|
|
|
|
this._dragOverItem = null;
|
|
|
|
}
|
2014-02-25 23:07:02 -08:00
|
|
|
this._updateToolbarCustomizationOutline(this.window);
|
2013-06-03 15:35:30 -07:00
|
|
|
this._showPanelCustomizationPlaceholders();
|
2014-04-09 14:37:38 -07:00
|
|
|
DragPositionManager.stop();
|
2013-05-16 21:25:40 -07:00
|
|
|
},
|
|
|
|
|
2013-09-10 11:37:23 -07:00
|
|
|
_isUnwantedDragDrop: function(aEvent) {
|
|
|
|
// The simulated events generated by synthesizeDragStart/synthesizeDrop in
|
|
|
|
// mochitests are used only for testing whether the right data is being put
|
|
|
|
// into the dataTransfer. Neither cause a real drop to occur, so they don't
|
|
|
|
// set the source node. There isn't a means of testing real drag and drops,
|
|
|
|
// so this pref skips the check but it should only be set by test code.
|
|
|
|
if (this._skipSourceNodeCheck) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
return !mozSourceNode ||
|
|
|
|
mozSourceNode.ownerDocument.defaultView != this.window;
|
|
|
|
},
|
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
_setDragActive: function(aItem, aValue, aDraggedItemId, aInToolbar) {
|
2013-06-03 13:13:34 -07:00
|
|
|
if (!aItem) {
|
|
|
|
return;
|
|
|
|
}
|
2013-03-22 12:26:26 -07:00
|
|
|
|
2014-02-03 08:38:00 -08:00
|
|
|
if (aItem.getAttribute("dragover") != aValue) {
|
2013-11-13 05:20:32 -08:00
|
|
|
aItem.setAttribute("dragover", aValue);
|
2013-05-29 13:41:44 -07:00
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
let window = aItem.ownerDocument.defaultView;
|
|
|
|
let draggedItem = window.document.getElementById(aDraggedItemId);
|
|
|
|
if (!aInToolbar) {
|
2014-01-15 15:44:48 -08:00
|
|
|
this._setGridDragActive(aItem, draggedItem, aValue);
|
2013-11-13 05:20:32 -08:00
|
|
|
} else {
|
2014-02-03 08:38:00 -08:00
|
|
|
let targetArea = this._getCustomizableParent(aItem);
|
2014-02-25 23:07:02 -08:00
|
|
|
this._updateToolbarCustomizationOutline(window, targetArea);
|
2014-02-03 08:38:00 -08:00
|
|
|
let makeSpaceImmediately = false;
|
|
|
|
if (!gDraggingInToolbars.has(targetArea.id)) {
|
|
|
|
gDraggingInToolbars.add(targetArea.id);
|
|
|
|
let draggedWrapper = this.document.getElementById("wrapper-" + aDraggedItemId);
|
|
|
|
let originArea = this._getCustomizableParent(draggedWrapper);
|
|
|
|
makeSpaceImmediately = originArea == targetArea;
|
|
|
|
}
|
2013-08-26 15:23:18 -07:00
|
|
|
// Calculate width of the item when it'd be dropped in this position
|
2013-11-13 05:20:32 -08:00
|
|
|
let width = this._getDragItemSize(aItem, draggedItem).width;
|
|
|
|
let direction = window.getComputedStyle(aItem).direction;
|
|
|
|
let prop, otherProp;
|
|
|
|
// If we're inserting before in ltr, or after in rtl:
|
|
|
|
if ((aValue == "before") == (direction == "ltr")) {
|
|
|
|
prop = "borderLeftWidth";
|
|
|
|
otherProp = "border-right-width";
|
|
|
|
} else {
|
|
|
|
// otherwise:
|
|
|
|
prop = "borderRightWidth";
|
|
|
|
otherProp = "border-left-width";
|
2013-05-29 13:41:44 -07:00
|
|
|
}
|
2014-02-03 08:38:00 -08:00
|
|
|
if (makeSpaceImmediately) {
|
|
|
|
aItem.setAttribute("notransition", "true");
|
|
|
|
}
|
2013-11-13 05:20:32 -08:00
|
|
|
aItem.style[prop] = width + 'px';
|
|
|
|
aItem.style.removeProperty(otherProp);
|
2014-02-03 08:38:00 -08:00
|
|
|
if (makeSpaceImmediately) {
|
|
|
|
// Force a layout flush:
|
|
|
|
aItem.getBoundingClientRect();
|
|
|
|
aItem.removeAttribute("notransition");
|
|
|
|
}
|
2013-03-22 12:26:26 -07:00
|
|
|
}
|
2013-11-13 05:20:32 -08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
_cancelDragActive: function(aItem, aNextItem, aNoTransition) {
|
2014-02-25 23:07:02 -08:00
|
|
|
this._updateToolbarCustomizationOutline(aItem.ownerDocument.defaultView);
|
2013-11-13 05:20:32 -08:00
|
|
|
let currentArea = this._getCustomizableParent(aItem);
|
|
|
|
if (!currentArea) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let isToolbar = CustomizableUI.getAreaType(currentArea.id) == "toolbar";
|
|
|
|
if (isToolbar) {
|
2014-02-03 08:38:00 -08:00
|
|
|
if (aNoTransition) {
|
|
|
|
aItem.setAttribute("notransition", "true");
|
|
|
|
}
|
2013-10-15 07:32:25 -07:00
|
|
|
aItem.removeAttribute("dragover");
|
2013-05-29 13:41:44 -07:00
|
|
|
// Remove both property values in the case that the end padding
|
|
|
|
// had been set.
|
2013-10-15 07:32:25 -07:00
|
|
|
aItem.style.removeProperty("border-left-width");
|
|
|
|
aItem.style.removeProperty("border-right-width");
|
2014-02-03 08:38:00 -08:00
|
|
|
if (aNoTransition) {
|
|
|
|
// Force a layout flush:
|
|
|
|
aItem.getBoundingClientRect();
|
|
|
|
aItem.removeAttribute("notransition");
|
|
|
|
}
|
2013-11-13 05:20:32 -08:00
|
|
|
} else {
|
2014-02-03 08:38:00 -08:00
|
|
|
aItem.removeAttribute("dragover");
|
2013-11-13 05:20:32 -08:00
|
|
|
if (aNextItem) {
|
|
|
|
let nextArea = this._getCustomizableParent(aNextItem);
|
|
|
|
if (nextArea == currentArea) {
|
|
|
|
// No need to do anything if we're still dragging in this area:
|
2013-11-18 09:46:04 -08:00
|
|
|
return;
|
2013-11-13 05:20:32 -08:00
|
|
|
}
|
2013-11-18 09:46:04 -08:00
|
|
|
}
|
2013-11-13 05:20:32 -08:00
|
|
|
// Otherwise, clear everything out:
|
|
|
|
let positionManager = DragPositionManager.getManagerForArea(currentArea);
|
|
|
|
positionManager.clearPlaceholders(currentArea, aNoTransition);
|
2013-11-18 09:46:04 -08:00
|
|
|
}
|
2013-11-13 05:20:32 -08:00
|
|
|
},
|
|
|
|
|
2014-01-15 15:44:48 -08:00
|
|
|
_setGridDragActive: function(aDragOverNode, aDraggedItem, aValue) {
|
2013-11-13 05:20:32 -08:00
|
|
|
let targetArea = this._getCustomizableParent(aDragOverNode);
|
2014-01-27 10:44:34 -08:00
|
|
|
let draggedWrapper = this.document.getElementById("wrapper-" + aDraggedItem.id);
|
|
|
|
let originArea = this._getCustomizableParent(draggedWrapper);
|
2013-11-13 05:20:32 -08:00
|
|
|
let positionManager = DragPositionManager.getManagerForArea(targetArea);
|
|
|
|
let draggedSize = this._getDragItemSize(aDragOverNode, aDraggedItem);
|
|
|
|
let isWide = aDraggedItem.classList.contains(CustomizableUI.WIDE_PANEL_CLASS);
|
2014-01-27 10:44:34 -08:00
|
|
|
positionManager.insertPlaceholder(targetArea, aDragOverNode, isWide, draggedSize,
|
|
|
|
originArea == targetArea);
|
2013-11-13 05:20:32 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
_getDragItemSize: function(aDragOverNode, aDraggedItem) {
|
2013-08-26 15:23:18 -07:00
|
|
|
// Cache it good, cache it real good.
|
2013-11-13 05:20:32 -08:00
|
|
|
if (!this._dragSizeMap)
|
|
|
|
this._dragSizeMap = new WeakMap();
|
|
|
|
if (!this._dragSizeMap.has(aDraggedItem))
|
|
|
|
this._dragSizeMap.set(aDraggedItem, new WeakMap());
|
|
|
|
let itemMap = this._dragSizeMap.get(aDraggedItem);
|
2013-08-26 15:23:18 -07:00
|
|
|
let targetArea = this._getCustomizableParent(aDragOverNode);
|
2013-11-13 05:20:32 -08:00
|
|
|
let currentArea = this._getCustomizableParent(aDraggedItem);
|
|
|
|
// Return the size for this target from cache, if it exists.
|
|
|
|
let size = itemMap.get(targetArea);
|
|
|
|
if (size)
|
|
|
|
return size;
|
2013-08-26 15:23:18 -07:00
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
// Calculate size of the item when it'd be dropped in this position.
|
2013-08-26 15:23:18 -07:00
|
|
|
let currentParent = aDraggedItem.parentNode;
|
|
|
|
let currentSibling = aDraggedItem.nextSibling;
|
2013-11-18 09:46:04 -08:00
|
|
|
const kAreaType = "cui-areatype";
|
2013-11-13 05:20:32 -08:00
|
|
|
let areaType, currentType;
|
|
|
|
|
|
|
|
if (targetArea != currentArea) {
|
|
|
|
// Move the widget temporarily next to the placeholder.
|
|
|
|
aDragOverNode.parentNode.insertBefore(aDraggedItem, aDragOverNode);
|
|
|
|
// Update the node's areaType.
|
|
|
|
areaType = CustomizableUI.getAreaType(targetArea.id);
|
|
|
|
currentType = aDraggedItem.hasAttribute(kAreaType) &&
|
|
|
|
aDraggedItem.getAttribute(kAreaType);
|
|
|
|
if (areaType)
|
|
|
|
aDraggedItem.setAttribute(kAreaType, areaType);
|
|
|
|
this.wrapToolbarItem(aDraggedItem, areaType || "palette");
|
|
|
|
CustomizableUI.onWidgetDrag(aDraggedItem.id, targetArea.id);
|
|
|
|
} else {
|
|
|
|
aDraggedItem.parentNode.hidden = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch the new size.
|
|
|
|
let rect = aDraggedItem.parentNode.getBoundingClientRect();
|
|
|
|
size = {width: rect.width, height: rect.height};
|
|
|
|
// Cache the found value of size for this target.
|
|
|
|
itemMap.set(targetArea, size);
|
|
|
|
|
|
|
|
if (targetArea != currentArea) {
|
|
|
|
this.unwrapToolbarItem(aDraggedItem.parentNode);
|
|
|
|
// Put the item back into its previous position.
|
2014-03-11 05:29:25 -07:00
|
|
|
currentParent.insertBefore(aDraggedItem, currentSibling);
|
2013-11-13 05:20:32 -08:00
|
|
|
// restore the areaType
|
|
|
|
if (areaType) {
|
|
|
|
if (currentType === false)
|
|
|
|
aDraggedItem.removeAttribute(kAreaType);
|
|
|
|
else
|
|
|
|
aDraggedItem.setAttribute(kAreaType, currentType);
|
|
|
|
}
|
2014-03-11 05:29:25 -07:00
|
|
|
this.createOrUpdateWrapper(aDraggedItem, null, true);
|
2013-11-13 05:20:32 -08:00
|
|
|
CustomizableUI.onWidgetDrag(aDraggedItem.id);
|
|
|
|
} else {
|
|
|
|
aDraggedItem.parentNode.hidden = true;
|
2013-08-26 15:23:18 -07:00
|
|
|
}
|
2013-11-13 05:20:32 -08:00
|
|
|
return size;
|
2013-08-26 15:23:18 -07:00
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
_getCustomizableParent: function(aElement) {
|
|
|
|
let areas = CustomizableUI.areas;
|
|
|
|
areas.push(kPaletteId);
|
|
|
|
while (aElement) {
|
|
|
|
if (areas.indexOf(aElement.id) != -1) {
|
|
|
|
return aElement;
|
|
|
|
}
|
|
|
|
aElement = aElement.parentNode;
|
|
|
|
}
|
|
|
|
return null;
|
2013-03-22 14:28:34 -07:00
|
|
|
},
|
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
_getDragOverNode: function(aEvent, aAreaElement, aInToolbar, aDraggedItemId) {
|
2013-05-30 01:07:43 -07:00
|
|
|
let expectedParent = aAreaElement.customizationTarget || aAreaElement;
|
2013-10-31 10:02:49 -07:00
|
|
|
// Our tests are stupid. Cope:
|
|
|
|
if (!aEvent.clientX && !aEvent.clientY) {
|
|
|
|
return aEvent.target;
|
|
|
|
}
|
|
|
|
// Offset the drag event's position with the offset to the center of
|
|
|
|
// the thing we're dragging
|
|
|
|
let dragX = aEvent.clientX - this._dragOffset.x;
|
|
|
|
let dragY = aEvent.clientY - this._dragOffset.y;
|
|
|
|
|
|
|
|
// Ensure this is within the container
|
2014-02-25 14:57:22 -08:00
|
|
|
let boundsContainer = expectedParent;
|
|
|
|
// NB: because the panel UI itself is inside a scrolling container, we need
|
|
|
|
// to use the parent bounds (otherwise, if the panel UI is scrolled down,
|
|
|
|
// the numbers we get are in window coordinates which leads to various kinds
|
|
|
|
// of weirdness)
|
|
|
|
if (boundsContainer == this.panelUIContents) {
|
|
|
|
boundsContainer = boundsContainer.parentNode;
|
|
|
|
}
|
|
|
|
let bounds = boundsContainer.getBoundingClientRect();
|
2013-10-31 10:02:49 -07:00
|
|
|
dragX = Math.min(bounds.right, Math.max(dragX, bounds.left));
|
|
|
|
dragY = Math.min(bounds.bottom, Math.max(dragY, bounds.top));
|
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
let targetNode;
|
|
|
|
if (aInToolbar) {
|
|
|
|
targetNode = aAreaElement.ownerDocument.elementFromPoint(dragX, dragY);
|
|
|
|
while (targetNode && targetNode.parentNode != expectedParent) {
|
|
|
|
targetNode = targetNode.parentNode;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let positionManager = DragPositionManager.getManagerForArea(aAreaElement);
|
2014-02-25 14:57:22 -08:00
|
|
|
// Make it relative to the container:
|
|
|
|
dragX -= bounds.left;
|
|
|
|
// NB: but if we're in the panel UI, we need to use the actual panel
|
|
|
|
// contents instead of the scrolling container to determine our origin
|
|
|
|
// offset against:
|
|
|
|
if (expectedParent == this.panelUIContents) {
|
|
|
|
dragY -= this.panelUIContents.getBoundingClientRect().top;
|
|
|
|
} else {
|
|
|
|
dragY -= bounds.top;
|
|
|
|
}
|
2013-11-13 05:20:32 -08:00
|
|
|
// Find the closest node:
|
|
|
|
targetNode = positionManager.find(aAreaElement, dragX, dragY, aDraggedItemId);
|
2013-05-30 01:07:43 -07:00
|
|
|
}
|
2013-10-31 10:02:49 -07:00
|
|
|
return targetNode || aEvent.target;
|
2013-05-30 01:07:43 -07:00
|
|
|
},
|
|
|
|
|
2013-03-22 14:28:34 -07:00
|
|
|
_onMouseDown: function(aEvent) {
|
2013-05-16 21:25:40 -07:00
|
|
|
LOG("_onMouseDown");
|
2013-11-21 20:09:34 -08:00
|
|
|
if (aEvent.button != 0) {
|
|
|
|
return;
|
|
|
|
}
|
2013-05-16 21:25:40 -07:00
|
|
|
let doc = aEvent.target.ownerDocument;
|
|
|
|
doc.documentElement.setAttribute("customizing-movingItem", true);
|
2013-03-22 14:28:34 -07:00
|
|
|
let item = this._getWrapper(aEvent.target);
|
2014-02-07 10:11:55 -08:00
|
|
|
if (item && !item.classList.contains(kPlaceholderClass) &&
|
|
|
|
item.getAttribute("removable") == "true") {
|
2013-03-22 14:28:34 -07:00
|
|
|
item.setAttribute("mousedown", "true");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_onMouseUp: function(aEvent) {
|
2013-05-16 21:25:40 -07:00
|
|
|
LOG("_onMouseUp");
|
2013-11-21 20:09:34 -08:00
|
|
|
if (aEvent.button != 0) {
|
|
|
|
return;
|
|
|
|
}
|
2013-05-16 21:25:40 -07:00
|
|
|
let doc = aEvent.target.ownerDocument;
|
|
|
|
doc.documentElement.removeAttribute("customizing-movingItem");
|
2013-03-22 14:28:34 -07:00
|
|
|
let item = this._getWrapper(aEvent.target);
|
|
|
|
if (item) {
|
|
|
|
item.removeAttribute("mousedown");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_getWrapper: function(aElement) {
|
|
|
|
while (aElement && aElement.localName != "toolbarpaletteitem") {
|
|
|
|
if (aElement.localName == "toolbar")
|
|
|
|
return null;
|
|
|
|
aElement = aElement.parentNode;
|
|
|
|
}
|
|
|
|
return aElement;
|
2013-04-10 10:37:25 -07:00
|
|
|
},
|
|
|
|
|
2013-06-03 15:35:30 -07:00
|
|
|
_showPanelCustomizationPlaceholders: function() {
|
|
|
|
let doc = this.document;
|
|
|
|
let contents = this.panelUIContents;
|
2013-10-22 15:08:56 -07:00
|
|
|
let narrowItemsAfterWideItem = 0;
|
|
|
|
let node = contents.lastChild;
|
2013-11-13 05:20:32 -08:00
|
|
|
while (node && !node.classList.contains(CustomizableUI.WIDE_PANEL_CLASS) &&
|
|
|
|
(!node.firstChild || !node.firstChild.classList.contains(CustomizableUI.WIDE_PANEL_CLASS))) {
|
|
|
|
if (!node.hidden && !node.classList.contains(kPlaceholderClass)) {
|
2013-10-22 15:08:56 -07:00
|
|
|
narrowItemsAfterWideItem++;
|
|
|
|
}
|
|
|
|
node = node.previousSibling;
|
|
|
|
}
|
|
|
|
|
2013-11-13 05:20:32 -08:00
|
|
|
let orphanedItems = narrowItemsAfterWideItem % CustomizableUI.PANEL_COLUMN_COUNT;
|
|
|
|
let placeholders = CustomizableUI.PANEL_COLUMN_COUNT - orphanedItems;
|
|
|
|
|
|
|
|
let currentPlaceholderCount = contents.querySelectorAll("." + kPlaceholderClass).length;
|
|
|
|
if (placeholders > currentPlaceholderCount) {
|
|
|
|
while (placeholders-- > currentPlaceholderCount) {
|
|
|
|
let placeholder = doc.createElement("toolbarpaletteitem");
|
|
|
|
placeholder.classList.add(kPlaceholderClass);
|
|
|
|
//XXXjaws The toolbarbutton child here is only necessary to get
|
|
|
|
// the styling right here.
|
|
|
|
let placeholderChild = doc.createElement("toolbarbutton");
|
|
|
|
placeholderChild.classList.add(kPlaceholderClass + "-child");
|
|
|
|
placeholder.appendChild(placeholderChild);
|
|
|
|
contents.appendChild(placeholder);
|
|
|
|
}
|
|
|
|
} else if (placeholders < currentPlaceholderCount) {
|
|
|
|
while (placeholders++ < currentPlaceholderCount) {
|
|
|
|
contents.querySelectorAll("." + kPlaceholderClass)[0].remove();
|
|
|
|
}
|
2013-06-03 15:35:30 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_removePanelCustomizationPlaceholders: function() {
|
|
|
|
let contents = this.panelUIContents;
|
|
|
|
let oldPlaceholders = contents.getElementsByClassName(kPlaceholderClass);
|
|
|
|
while (oldPlaceholders.length) {
|
|
|
|
contents.removeChild(oldPlaceholders[0]);
|
|
|
|
}
|
2014-02-25 23:07:02 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update toolbar customization targets during drag events to add or remove
|
|
|
|
* outlines to indicate that an area is customizable.
|
|
|
|
*
|
|
|
|
* @param aWindow The XUL window in which outlines should be updated.
|
|
|
|
* @param {Element} [aToolbarArea=null] The element of the customizable toolbar area to add the
|
|
|
|
* outline to. If aToolbarArea is falsy, the outline will be
|
|
|
|
* removed from all toolbar areas.
|
|
|
|
*/
|
|
|
|
_updateToolbarCustomizationOutline: function(aWindow, aToolbarArea = null) {
|
|
|
|
// Remove the attribute from existing customization targets
|
|
|
|
for (let area of CustomizableUI.areas) {
|
|
|
|
if (CustomizableUI.getAreaType(area) != CustomizableUI.TYPE_TOOLBAR) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let target = CustomizableUI.getCustomizeTargetForArea(area, aWindow);
|
|
|
|
target.removeAttribute("customizing-dragovertarget");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now set the attribute on the desired target
|
|
|
|
if (aToolbarArea) {
|
|
|
|
if (CustomizableUI.getAreaType(aToolbarArea.id) != CustomizableUI.TYPE_TOOLBAR)
|
|
|
|
return;
|
|
|
|
let target = CustomizableUI.getCustomizeTargetForArea(aToolbarArea.id, aWindow);
|
|
|
|
target.setAttribute("customizing-dragovertarget", true);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-03-05 12:03:00 -08:00
|
|
|
_findVisiblePreviousSiblingNode: function(aReferenceNode) {
|
|
|
|
while (aReferenceNode &&
|
|
|
|
aReferenceNode.localName == "toolbarpaletteitem" &&
|
|
|
|
aReferenceNode.firstChild.hidden) {
|
|
|
|
aReferenceNode = aReferenceNode.previousSibling;
|
|
|
|
}
|
|
|
|
return aReferenceNode;
|
|
|
|
},
|
2013-03-26 14:23:23 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
function __dumpDragData(aEvent, caller) {
|
2013-11-13 05:20:32 -08:00
|
|
|
if (!gDebug) {
|
|
|
|
return;
|
|
|
|
}
|
2014-03-04 08:12:41 -08:00
|
|
|
let str = "Dumping drag data (" + (caller ? caller + " in " : "") + "CustomizeMode.jsm) {\n";
|
2013-03-26 14:23:23 -07:00
|
|
|
str += " type: " + aEvent["type"] + "\n";
|
|
|
|
for (let el of ["target", "currentTarget", "relatedTarget"]) {
|
|
|
|
if (aEvent[el]) {
|
|
|
|
str += " " + el + ": " + aEvent[el] + "(localName=" + aEvent[el].localName + "; id=" + aEvent[el].id + ")\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (let prop in aEvent.dataTransfer) {
|
|
|
|
if (typeof aEvent.dataTransfer[prop] != "function") {
|
|
|
|
str += " dataTransfer[" + prop + "]: " + aEvent.dataTransfer[prop] + "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
str += "}";
|
|
|
|
LOG(str);
|
|
|
|
}
|
2013-06-17 07:37:41 -07:00
|
|
|
|
|
|
|
function dispatchFunction(aFunc) {
|
|
|
|
Services.tm.currentThread.dispatch(aFunc, Ci.nsIThread.DISPATCH_NORMAL);
|
|
|
|
}
|