constructor()

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();
      });
    }