this._tabMinWidth = newValue,
newValue => {
const LIMIT = 50;
return Math.max(newValue, LIMIT);
},
);
this._tabMinWidth = this._tabMinWidthPref;
this._setPositionalAttributes();
CustomizableUI.addListener(this);
this._updateNewTabVisibility();
XPCOMUtils.defineLazyPreferenceGetter(this, "_closeTabByDblclick",
"browser.tabs.closeTabByDblclick", false);
]]>
document.getElementById("tabbrowser-tabbox");
document.getElementById("tabContextMenu");
document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
null
null
null
null
null
null
this.style.setProperty("--tab-min-width", val + "px");
return val;
2)) {
containersEnabled = false;
}
const newTab = document.getElementById("new-tab-button");
const newTab2 = document.getAnonymousElementByAttribute(this, "anonid", "tabs-newtab-button");
for (let parent of [newTab, newTab2]) {
if (!parent)
continue;
gClickAndHoldListenersOnElement.remove(parent);
parent.removeAttribute("type");
if (parent.firstChild) {
parent.firstChild.remove();
}
if (containersEnabled) {
let popup = document.createElementNS(
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"menupopup");
if (parent.id) {
popup.id = "newtab-popup";
} else {
popup.setAttribute("anonid", "newtab-popup");
}
popup.className = "new-tab-popup";
popup.setAttribute("position", "after_end");
parent.appendChild(popup);
// longPressBehavior == 2 means that the menu is shown after X
// millisecs. Otherwise, with 1, the menu is open immediatelly.
if (longPressBehavior == 2) {
gClickAndHoldListenersOnElement.add(parent);
}
parent.setAttribute("type", "menu");
}
}
break;
}
]]>
false
document.getAnonymousElementByAttribute(this, "anonid", "tab-drop-indicator");
350
0
false
{
window.requestAnimationFrame(() => {
this._closeButtonsUpdatePending = false;
// The scrollbox may have started overflowing since we checked
// overflow earlier, so check again.
if (this.getAttribute("overflow") == "true") {
this.setAttribute("closebuttons", "activetab");
return;
}
// Check if tab widths are below the threshold where we want to
// remove close buttons from background tabs so that people don't
// accidentally close tabs by selecting them.
let rect = ele => {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.getBoundsWithoutFlushing(ele);
};
let tab = this._getVisibleTabs()[gBrowser._numPinnedTabs];
if (tab && rect(tab).width <= this._tabClipWidth) {
this.setAttribute("closebuttons", "activetab");
} else {
this.removeAttribute("closebuttons");
}
});
});
]]>
document.getAnonymousElementByAttribute(this, "anonid", "closing-tabs-spacer");
NaN
false
false
tabs[tabs.length - 1]._tPos);
var tabWidth = aTab.getBoundingClientRect().width;
if (!this._tabDefaultMaxWidth) {
this._tabDefaultMaxWidth =
parseFloat(window.getComputedStyle(aTab).maxWidth);
}
this._lastTabClosedByMouse = true;
if (this.getAttribute("overflow") == "true") {
// Don't need to do anything if we're in overflow mode and aren't scrolled
// all the way to the right, or if we're closing the last tab.
if (isEndTab || !this.arrowScrollbox._scrollButtonDown.disabled) {
return;
}
// If the tab has an owner that will become the active tab, the owner will
// be to the left of it, so we actually want the left tab to slide over.
// This can't be done as easily in non-overflow mode, so we don't bother.
if (aTab.owner) {
return;
}
this._expandSpacerBy(tabWidth);
} else { // non-overflow mode
// Locking is neither in effect nor needed, so let tabs expand normally.
if (isEndTab && !this._hasTabTempMaxWidth) {
return;
}
let numPinned = gBrowser._numPinnedTabs;
// Force tabs to stay the same width, unless we're closing the last tab,
// which case we need to let them expand just enough so that the overall
// tabbar width is the same.
if (isEndTab) {
let numNormalTabs = tabs.length - numPinned;
tabWidth = tabWidth * (numNormalTabs + 1) / numNormalTabs;
if (tabWidth > this._tabDefaultMaxWidth) {
tabWidth = this._tabDefaultMaxWidth;
}
}
tabWidth += "px";
for (let i = numPinned; i < tabs.length; i++) {
let tab = tabs[i];
tab.style.setProperty("max-width", tabWidth, "important");
if (!isEndTab) { // keep tabs the same width
tab.style.transition = "none";
tab.clientTop; // flush styles to skip animation; see bug 649247
tab.style.transition = "";
}
}
this._hasTabTempMaxWidth = true;
gBrowser.addEventListener("mousemove", this);
window.addEventListener("mouseout", this);
}
]]>
0
null
numPinned &&
numPinned > 0;
if (doPosition) {
this.setAttribute("positionpinnedtabs", "true");
let layoutData = this._pinnedTabsLayoutCache;
let uiDensity = document.documentElement.getAttribute("uidensity");
if (!layoutData ||
layoutData.uiDensity != uiDensity) {
let arrowScrollbox = this.arrowScrollbox;
layoutData = this._pinnedTabsLayoutCache = {
uiDensity,
pinnedTabWidth: this.childNodes[0].getBoundingClientRect().width,
scrollButtonWidth: arrowScrollbox._scrollButtonDown.getBoundingClientRect().width
};
}
let width = 0;
for (let i = numPinned - 1; i >= 0; i--) {
let tab = this.childNodes[i];
width += layoutData.pinnedTabWidth;
tab.style.marginInlineStart = -(width + layoutData.scrollButtonWidth) + "px";
tab._pinnedUnscrollable = true;
}
this.style.paddingInlineStart = width + "px";
} else {
this.removeAttribute("positionpinnedtabs");
for (let i = 0; i < numPinned; i++) {
let tab = this.childNodes[i];
tab.style.marginInlineStart = "";
tab._pinnedUnscrollable = false;
}
this.style.paddingInlineStart = "";
}
if (this._lastNumPinned != numPinned) {
this._lastNumPinned = numPinned;
this._handleTabSelect(true);
}
]]>
high)
break;
let boxObject = tabs[mid].boxObject;
screenX = boxObject.screenX + getTabShift(tabs[mid], oldIndex);
if (screenX > tabCenter) {
high = mid - 1;
} else if (screenX + boxObject.width < tabCenter) {
low = mid + 1;
} else {
newIndex = tabs[mid]._tPos;
break;
}
}
if (newIndex >= oldIndex)
newIndex++;
if (newIndex < 0 || newIndex == oldIndex)
return;
draggedTab._dragData.animDropIndex = newIndex;
// Shift background tabs to leave a gap where the dragged tab
// would currently be dropped.
for (let tab of tabs) {
if (tab != draggedTab) {
let shift = getTabShift(tab, newIndex);
tab.style.transform = shift ? "translateX(" + shift + "px)" : "";
}
}
function getTabShift(tab, dropIndex) {
if (tab._tPos < draggedTab._tPos && tab._tPos >= dropIndex)
return rtl ? -tabWidth : tabWidth;
if (tab._tPos > draggedTab._tPos && tab._tPos < dropIndex)
return rtl ? tabWidth : -tabWidth;
return 0;
}
]]>
this.arrowScrollbox._scrollButtonDown;
boxObject.screenX + boxObject.width * .75)
return null;
}
return tab;
]]>
tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
return i;
}
return tabs.length;
]]>
n.parentNode.localName == "toolbarpaletteitem" ? n.parentNode : n;
let unwrap = n => n && n.localName == "toolbarpaletteitem" ? n.firstElementChild : n;
// Starting from the tabs element, find the next sibling that:
// - isn't hidden; and
// - isn't one of the titlebar placeholder elements; and
// - isn't the all-tabs button.
// If it's the new tab button, consider the new tab button adjacent to the tabs.
// If the new tab button is marked as adjacent and the tabstrip doesn't
// overflow, we'll display the 'new tab' button inline in the tabstrip.
// In all other cases, the separate new tab button is displayed in its
// customized location.
let sib = this;
do {
sib = unwrap(wrap(sib).nextElementSibling);
} while (sib && (sib.hidden ||
sib.getAttribute("skipintoolbarset") == "true" ||
sib.id == "alltabs-button"));
const kAttr = "hasadjacentnewtabbutton";
if (sib && sib.id == "new-tab-button") {
this.setAttribute(kAttr, "true");
} else {
this.removeAttribute(kAttr);
}
]]>
1 && !target._ignoredCloseButtonClicks) {
target._ignoredCloseButtonClicks = true;
event.stopPropagation();
return;
} else {
// Reset the "ignored click" flag
target._ignoredCloseButtonClicks = false;
}
}
/* Protects from close-tab-button errant doubleclick:
* Since we're removing the event target, if the user
* double-clicks the button, the dblclick event will be dispatched
* with the tabbar as its event target (and explicit/originalTarget),
* which treats that as a mouse gesture for opening a new tab.
* In this context, we're manually blocking the dblclick event.
*/
if (this._blockDblClick) {
if (!("_clickedTabBarOnce" in this)) {
this._clickedTabBarOnce = true;
return;
}
delete this._clickedTabBarOnce;
this._blockDblClick = false;
}
]]>
endOfTab) ||
(!ltr && event.clientX < endOfTab)) {
BrowserOpenTab();
}
} else {
BrowserOpenTab();
}
} else {
return;
}
event.stopPropagation();
]]>
= this._dragTime + this._dragOverDelay)
this.selectedItem = tab;
ind.collapsed = true;
return;
}
}
var rect = arrowScrollbox.getBoundingClientRect();
var newMargin;
if (pixelsToScroll) {
// if we are scrolling, put the drop indicator at the edge
// so that it doesn't jump while scrolling
let scrollRect = arrowScrollbox.scrollClientRect;
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];
newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin;
} else {
let newIndex = this._getDropIndex(event, effects == "link");
if (newIndex == this.childNodes.length) {
let tabRect = this.childNodes[newIndex - 1].getBoundingClientRect();
if (ltr)
newMargin = tabRect.right - rect.left;
else
newMargin = rect.right - tabRect.left;
} else {
let tabRect = this.childNodes[newIndex].getBoundingClientRect();
if (ltr)
newMargin = tabRect.left - rect.left;
else
newMargin = rect.right - tabRect.right;
}
}
ind.collapsed = false;
newMargin += ind.clientWidth / 2;
if (!ltr)
newMargin *= -1;
ind.style.transform = "translate(" + Math.round(newMargin) + "px)";
ind.style.marginInlineStart = (-ind.clientWidth) + "px";
]]>
0 && translateOffset > tabWidth / 2) {
newTranslateX += tabWidth;
} else if (oldTranslateX < 0 && -translateOffset > tabWidth / 2) {
newTranslateX -= tabWidth;
}
let dropIndex = "animDropIndex" in draggedTab._dragData &&
draggedTab._dragData.animDropIndex;
if (dropIndex && dropIndex > draggedTab._tPos)
dropIndex--;
let animate = gBrowser.animationsEnabled;
if (oldTranslateX && oldTranslateX != newTranslateX && animate) {
draggedTab.setAttribute("tabdrop-samewindow", "true");
draggedTab.style.transform = "translateX(" + newTranslateX + "px)";
let onTransitionEnd = transitionendEvent => {
if (transitionendEvent.propertyName != "transform" ||
transitionendEvent.originalTarget != draggedTab) {
return;
}
draggedTab.removeEventListener("transitionend", onTransitionEnd);
draggedTab.removeAttribute("tabdrop-samewindow");
this._finishAnimateTabMove();
if (dropIndex !== false) {
gBrowser.moveTabTo(draggedTab, dropIndex);
}
gBrowser.syncThrobberAnimations(draggedTab);
};
draggedTab.addEventListener("transitionend", onTransitionEnd);
} else {
this._finishAnimateTabMove();
if (dropIndex !== false) {
gBrowser.moveTabTo(draggedTab, dropIndex);
}
}
} else if (draggedTab) {
let newIndex = this._getDropIndex(event, false);
gBrowser.adoptTab(draggedTab, newIndex, true);
} else {
// Pass true to disallow dropping javascript: or data: urls
let links;
try {
links = browserDragAndDrop.dropLinks(event, true);
} catch (ex) {}
if (!links || links.length === 0)
return;
let inBackground = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
if (event.shiftKey)
inBackground = !inBackground;
let targetTab = this._getDragTargetTab(event, true);
let userContextId = this.selectedItem.getAttribute("usercontextid");
let replace = !!targetTab;
let newIndex = this._getDropIndex(event, true);
let urls = links.map(link => link.url);
let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(event);
(async () => {
if (urls.length >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
// Sync dialog cannot be used inside drop event handler.
let answer = await OpenInTabsUtils.promiseConfirmOpenInTabs(urls.length,
window);
if (!answer) {
return;
}
}
gBrowser.loadTabs(urls, {
inBackground,
replace,
allowThirdPartyFixup: true,
targetTab,
newIndex,
userContextId,
triggeringPrincipal,
});
})();
}
if (draggedTab) {
delete draggedTab._dragData;
}
]]>
wX && eX < (wX + window.outerWidth)) {
let bo = this.arrowScrollbox.boxObject;
// also avoid detaching if the the tab was dropped too close to
// the tabbar (half a tab)
let endScreenY = bo.screenY + 1.5 * bo.height;
if (eY < endScreenY && eY > window.screenY)
return;
}
// screen.availLeft et. al. only check the screen that this window is on,
// but we want to look at the screen the tab is being dropped onto.
var screen = Cc["@mozilla.org/gfx/screenmanager;1"]
.getService(Ci.nsIScreenManager)
.screenForRect(eX, eY, 1, 1);
var fullX = {}, fullY = {}, fullWidth = {}, fullHeight = {};
var availX = {}, availY = {}, availWidth = {}, availHeight = {};
// get full screen rect and available rect, both in desktop pix
screen.GetRectDisplayPix(fullX, fullY, fullWidth, fullHeight);
screen.GetAvailRectDisplayPix(availX, availY, availWidth, availHeight);
// scale factor to convert desktop pixels to CSS px
var scaleFactor =
screen.contentsScaleFactor / screen.defaultCSSScaleFactor;
// synchronize CSS-px top-left coordinates with the screen's desktop-px
// coordinates, to ensure uniqueness across multiple screens
// (compare the equivalent adjustments in nsGlobalWindow::GetScreenXY()
// and related methods)
availX.value = (availX.value - fullX.value) * scaleFactor + fullX.value;
availY.value = (availY.value - fullY.value) * scaleFactor + fullY.value;
availWidth.value *= scaleFactor;
availHeight.value *= scaleFactor;
// ensure new window entirely within screen
var winWidth = Math.min(window.outerWidth, availWidth.value);
var winHeight = Math.min(window.outerHeight, availHeight.value);
var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, availX.value),
availX.value + availWidth.value - winWidth);
var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, availY.value),
availY.value + availHeight.value - winHeight);
delete draggedTab._dragData;
if (gBrowser.tabs.length == 1) {
// resize _before_ move to ensure the window fits the new screen. if
// the window is too large for its screen, the window manager may do
// automatic repositioning.
window.resizeTo(winWidth, winHeight);
window.moveTo(left, top);
window.focus();
} else {
let props = { screenX: left, screenY: top, suppressanimation: 1 };
if (AppConstants.platform != "win") {
props.outerWidth = winWidth;
props.outerHeight = winHeight;
}
gBrowser.replaceTabWithWindow(draggedTab, props);
}
event.stopPropagation();
]]>
false
return this.getAttribute("pinned") == "true";
return this.getAttribute("hidden") == "true";
return this.getAttribute("muted") == "true";
return this.getAttribute("multiselected") == "true";
return this.getAttribute("before-multiselected") == "true";
undefined
return this.hasAttribute("usercontextid")
? parseInt(this.getAttribute("usercontextid"))
: 0;
return this.getAttribute("soundplaying") == "true";
return this.getAttribute("activemedia-blocked") == "true";
return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
false
null
{
if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
TelemetryStopwatch.cancel("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
}
}, 100);
]]>
this.style.MozUserFocus = "";
this.style.MozUserFocus = "";
0 && !overCloseButton && !this._overPlayingIcon) {
// Tabs were previously multi-selected and user clicks on a tab
// without holding Ctrl/Cmd Key
// Force positional attributes to update when the
// target (of the click) is the "active" tab.
let updatePositionalAttr = gBrowser.selectedTab == this;
gBrowser.clearMultiSelectedTabs(updatePositionalAttr);
}
}
if (this._overPlayingIcon) {
if (this.multiselected) {
gBrowser.toggleMuteAudioOnMultiSelectedTabs(this);
} else {
this.toggleMuteAudio();
}
return;
}
if (event.originalTarget.getAttribute("anonid") == "close-button") {
if (this.multiselected) {
gBrowser.removeMultiSelectedTabs();
} else {
gBrowser.removeTab(this, {
animate: true,
byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE,
});
}
// This enables double-click protection for the tab container
// (see tabbrowser-tabs 'click' handler).
gBrowser.tabContainer._blockDblClick = true;
}
]]>
0
= this.childNodes.length)
return val;
let toTab = this.getRelatedElement(this.childNodes[val]);
gBrowser._getSwitcher().requestTab(toTab);
var panel = this._selectedPanel;
var newPanel = this.childNodes[val];
this._selectedPanel = newPanel;
if (this._selectedPanel != panel) {
var event = document.createEvent("Events");
event.initEvent("select", true, true);
this.dispatchEvent(event);
this._selectedIndex = val;
}
return val;
]]>
null
null