in mail/base/content/widgets/tabmail-tabs.js [21:349]
constructor() {
super();
this.addEventListener("dragstart", event => {
const draggedTab = this._getDragTargetTab(event);
if (!draggedTab) {
return;
}
const tab = this.tabmail.selectedTab;
if (!tab || !tab.canClose) {
return;
}
let dt = event.dataTransfer;
// If we drag within the same window, we use the tab directly
dt.mozSetDataAt("application/x-moz-tabmail-tab", draggedTab, 0);
// Otherwise we use session restore & JSON to migrate the tab.
let uri = this.tabmail.persistTab(tab);
// In case the tab implements session restore, we use JSON to convert
// it into a string.
//
// If a tab does not support session restore it returns null. We can't
// moved such tabs to a new window. However moving them within the same
// window works perfectly fine.
if (uri) {
uri = JSON.stringify(uri);
}
dt.mozSetDataAt("application/x-moz-tabmail-json", uri, 0);
dt.mozCursor = "default";
// Create Drag Image.
const panel = document.getElementById("tabpanelcontainer");
const thumbnail = document.createElementNS(
"http://www.w3.org/1999/xhtml",
"canvas"
);
thumbnail.width = Math.ceil(screen.availWidth / 5.75);
thumbnail.height = Math.round(thumbnail.width * 0.5625);
const snippetWidth = panel.getBoundingClientRect().width * 0.6;
const scale = thumbnail.width / snippetWidth;
const ctx = thumbnail.getContext("2d");
ctx.scale(scale, scale);
ctx.drawWindow(
window,
panel.screenX - window.mozInnerScreenX,
panel.screenY - window.mozInnerScreenY,
snippetWidth,
snippetWidth * 0.5625,
"rgb(255,255,255)"
);
dt = event.dataTransfer;
dt.setDragImage(thumbnail, 0, 0);
event.stopPropagation();
});
this.addEventListener("dragover", event => {
const dt = event.dataTransfer;
if (dt.mozItemCount == 0) {
return;
}
// Bug 516247:
// in case the user is dragging something else than a tab, and
// keeps hovering over a tab, we assume he wants to switch to this tab.
if (
dt.mozTypesAt(0)[0] != "application/x-moz-tabmail-tab" &&
dt.mozTypesAt(0)[1] != "application/x-moz-tabmail-json"
) {
const tab = this._getDragTargetTab(event);
if (!tab) {
return;
}
event.preventDefault();
event.stopPropagation();
if (!this._dragTime) {
this._dragTime = Date.now();
return;
}
if (Date.now() <= this._dragTime + this._dragOverDelay) {
return;
}
if (this.tabmail.tabContainer.selectedItem == tab) {
return;
}
this.tabmail.tabContainer.selectedItem = tab;
return;
}
// As some tabs do not support session restore they can't be
// moved to a different or new window. We should not show
// a dropmarker in such a case.
if (!dt.mozGetDataAt("application/x-moz-tabmail-json", 0)) {
const draggedTab = dt.mozGetDataAt(
"application/x-moz-tabmail-tab",
0
);
if (!draggedTab) {
return;
}
if (this.tabmail.tabContainer.getIndexOfItem(draggedTab) == -1) {
return;
}
}
dt.effectAllowed = "copyMove";
event.preventDefault();
event.stopPropagation();
const ltr = window.getComputedStyle(this).direction == "ltr";
const ind = this._tabDropIndicator;
const arrowScrollbox = this.arrowScrollbox;
// Let's scroll
let pixelsToScroll = 0;
if (arrowScrollbox.getAttribute("overflow") == "true") {
switch (event.target) {
case arrowScrollbox._scrollButtonDown:
pixelsToScroll = arrowScrollbox.scrollIncrement * -1;
break;
case arrowScrollbox._scrollButtonUp:
pixelsToScroll = arrowScrollbox.scrollIncrement;
break;
}
if (ltr) {
pixelsToScroll = pixelsToScroll * -1;
}
if (pixelsToScroll) {
// Hide Indicator while Scrolling
ind.hidden = true;
arrowScrollbox.scrollByPixels(pixelsToScroll);
return;
}
}
let newIndex = this._getDropIndex(event);
// Fix the DropIndex in case it points to tab that can't be closed.
const tabInfo = this.tabmail.tabInfo;
while (newIndex < tabInfo.length && !tabInfo[newIndex].canClose) {
newIndex++;
}
const scrollRect = this.arrowScrollbox.scrollClientRect;
const rect = this.getBoundingClientRect();
let minMargin = scrollRect.left - rect.left;
let maxMargin = Math.min(
minMargin + scrollRect.width,
scrollRect.right
);
if (!ltr) {
[minMargin, maxMargin] = [
this.clientWidth - maxMargin,
this.clientWidth - minMargin,
];
}
let newMargin;
const tabs = this.allTabs;
if (newIndex == tabs.length) {
const tabRect = tabs[newIndex - 1].getBoundingClientRect();
if (ltr) {
newMargin = tabRect.right - rect.left;
} else {
newMargin = rect.right - tabRect.left;
}
} else {
const tabRect = tabs[newIndex].getBoundingClientRect();
if (ltr) {
newMargin = tabRect.left - rect.left;
} else {
newMargin = rect.right - tabRect.right;
}
}
ind.hidden = false;
newMargin -= ind.clientWidth / 2;
ind.style.insetInlineStart = `${Math.round(newMargin)}px`;
});
this.addEventListener("drop", event => {
const dt = event.dataTransfer;
if (dt.mozItemCount != 1) {
return;
}
let draggedTab = dt.mozGetDataAt("application/x-moz-tabmail-tab", 0);
if (!draggedTab) {
return;
}
event.stopPropagation();
this._tabDropIndicator.hidden = true;
// Is the tab one of our children?
if (this.tabmail.tabContainer.getIndexOfItem(draggedTab) == -1) {
// It's a tab from an other window, so we have to trigger session
// restore to get our tab
const tabmail2 = draggedTab.ownerDocument.getElementById("tabmail");
if (!tabmail2) {
return;
}
let draggedJson = dt.mozGetDataAt(
"application/x-moz-tabmail-json",
0
);
if (!draggedJson) {
return;
}
draggedJson = JSON.parse(draggedJson);
// Some tab exist only once, so we have to gamble a bit. We close
// the tab and try to reopen it. If something fails the tab is gone.
tabmail2.closeTab(draggedTab, true);
if (!this.tabmail.restoreTab(draggedJson)) {
return;
}
draggedTab =
this.tabmail.tabContainer.allTabs[
this.tabmail.tabContainer.allTabs.length - 1
];
}
let idx = this._getDropIndex(event);
// Fix the DropIndex in case it points to tab that can't be closed
const tabInfo = this.tabmail.tabInfo;
while (idx < tabInfo.length && !tabInfo[idx].canClose) {
idx++;
}
this.tabmail.moveTabTo(draggedTab, idx);
this.tabmail.switchToTab(draggedTab);
this.tabmail.updateCurrentTab();
});
this.addEventListener("dragend", event => {
// Note: while this case is correctly handled here, this event
// isn't dispatched when the tab is moved within the tabstrip,
// see bug 460801.
// The user pressed ESC to cancel the drag, or the drag succeeded.
const dt = event.dataTransfer;
if (dt.mozUserCancelled || dt.dropEffect != "none") {
return;
}
// Disable detach within the browser toolbox.
const eX = event.screenX;
const wX = window.screenX;
// Check if the drop point is horizontally within the window.
if (eX > wX && eX < wX + window.outerWidth) {
const bo = this.arrowScrollbox;
// Also avoid detaching if the the tab was dropped too close to
// the tabbar (half a tab).
const endScreenY =
bo.screenY + 1.5 * bo.getBoundingClientRect().height;
const eY = event.screenY;
if (eY < endScreenY && eY > window.screenY) {
return;
}
}
// User wants to deatach tab from window...
if (dt.mozItemCount != 1) {
return;
}
const draggedTab = dt.mozGetDataAt("application/x-moz-tabmail-tab", 0);
if (!draggedTab) {
return;
}
this.tabmail.replaceTabWithWindow(draggedTab);
});
this.addEventListener("dragleave", event => {
this._dragTime = 0;
this._tabDropIndicator.hidden = true;
event.stopPropagation();
});
}