/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// This is loaded into chrome windows with the subscript loader. Wrap in // a block to prevent accidentally leaking globals onto `window`.
{ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
TabMetrics: "moz-src:///browser/components/tabbrowser/TabMetrics.sys.mjs",
});
if (!("_lastAccessed" in this)) { this.updateLastAccessed();
}
let labelContainer = this.querySelector(".tab-label-container");
labelContainer.addEventListener("overflow", this);
labelContainer.addEventListener("underflow", this);
// Tabs in the tab strip default to being at the top level (level 1) // Tabs in tab groups are one level down (level 2); this tab will // update its value when it moves in and out of tab groups. this.setAttribute("aria-level", 1);
}
#elementIndex;
get elementIndex() { if (!this.visible) { thrownew Error("Tab is not visible, so does not have an elementIndex");
} // Make sure the index is up to date. this.container.dragAndDropElements; returnthis.#elementIndex;
}
set elementIndex(index) { this.#elementIndex = index;
}
#owner;
get owner() {
let owner = this.#owner?.deref(); if (owner && !owner.closing) { return owner;
} returnnull;
}
set owner(owner) { this.#owner = owner ? new WeakRef(owner) : null;
}
get container() { return gBrowser.tabContainer;
}
set attention(val) { if (val == this.hasAttribute("attention")) { return;
}
set _selected(val) { // in e10s we want to only pseudo-select a tab before its rendering is done, so that // the rest of the system knows that the tab is selected, but we don't want to update its // visual status to selected until after we receive confirmation that its content has painted. if (val) { this.setAttribute("selected", "true");
} else { this.removeAttribute("selected");
}
// If we're non-e10s we need to update the visual selection at the same // time, otherwise AsyncTabSwitcher will take care of this. if (!gMultiProcessBrowser) { this._visuallySelected = val;
}
}
get pinned() { returnthis.hasAttribute("pinned");
}
get isOpen() { return ( this.isConnected && !this.closing && this != FirefoxViewHandler.tab
);
}
get animationsEnabled() { returnthis.style.transition == "";
}
set animationsEnabled(val) { this.style.transition = val ? "" : "none";
}
get isEmpty() { // Determines if a tab is "empty", usually used in the context of determining // if it's ok to close the tab. if (this.hasAttribute("busy")) { returnfalse;
}
if (this.hasAttribute("customizemode")) { returnfalse;
}
let browser = this.linkedBrowser; if (!isBlankPageURL(browser.currentURI.spec)) { returnfalse;
}
if (!BrowserUIUtils.checkEmptyPageOrigin(browser)) { returnfalse;
}
if (browser.canGoForward || browser.canGoBack) { returnfalse;
}
/** *Returnsatimestampwhichattemptstorepresentthelasttimetheusersawthistab. *Ifthetabhasnotbeenactiveinthissession,anylastAccessedisused.We *differentiatebetweenselectedandexplicitlyvisible;aselectedtabinahidden *windowislastseenwhenthatwindowandtabwerelastvisible. *Weusetheapplicationstarttimeasafallbackvaluewhennoothersuitablevalue *isavailable.
*/
get lastSeenActive() { const isForegroundWindow = this.documentGlobal ==
BrowserWindowTracker.getTopWindow({ allowPopups: true }); // the timestamp for the selected tab in the active window is always now if (isForegroundWindow && this.selected) { return Date.now();
} if (this._lastSeenActive) { returnthis._lastSeenActive;
}
if (
!this._lastAccessed || this._lastAccessed >= this.container.startupTime
) { // When the tab was created this session but hasn't been seen by the user, // default to the application start time. returnthis.container.startupTime;
} // The tab was restored from a previous session but never seen. // Use the lastAccessed as the best proxy for when the user might have seen it. returnthis._lastAccessed;
}
get _overPlayingIcon() { returnthis.overlayIcon?.matches(":hover");
}
get _overAudioButton() { returnthis.audioButton?.matches(":hover");
}
get overlayIcon() { returnthis.querySelector(".tab-icon-overlay");
}
get audioButton() { returnthis.querySelector(".tab-audio-button");
}
get throbber() { returnthis.querySelector(".tab-throbber");
}
get iconImage() { returnthis.querySelector(".tab-icon-image");
}
get sharingIcon() { returnthis.querySelector(".tab-sharing-icon-overlay");
}
get textLabel() { returnthis.querySelector(".tab-label");
}
get closeButton() { returnthis.querySelector(".tab-close-button");
}
get noteIcon() { returnthis.querySelector(".tab-note-icon");
}
get noteIconOverlay() { returnthis.querySelector(".tab-note-icon-overlay");
}
get group() { returnthis.closest("tab-group");
}
get splitview() { if (this.parentElement?.tagName == "tab-split-view-wrapper") { returnthis.parentElement;
} returnnull;
}
/** *@returns{boolean}
*/
get hasTabNote() { returnthis.hasAttribute("tab-note");
}
/** *@param{boolean}val
*/
set hasTabNote(val) { this.toggleAttribute("tab-note", val);
}
// If the new target is not part of this tab then this is a mouseleave event. if (!this.contains(event.relatedTarget)) { this._mouseleave();
}
}
on_dragstart(event) { // We use "failed" drag end events that weren't cancelled by the user // to detach tabs. Ensure that we do not show the drag image returning // to its point of origin when this happens, as it makes the drag // finishing feel very slow.
event.dataTransfer.mozShowFailAnimation = false; if (event.eventPhase == Event.CAPTURING_PHASE) { this.style.MozUserFocus = "";
} elseif (
event.target.classList?.contains("tab-close-button") ||
gSharedTabWarning.willShowSharedTabWarning(this)
) {
event.stopPropagation();
}
}
on_mousedown(event) {
let eventMaySelectTab = true;
let tabContainer = this.container;
if (gSharedTabWarning.willShowSharedTabWarning(this)) {
eventMaySelectTab = false;
}
if (eventMaySelectTab) { super.on_mousedown(event);
}
}
on_mouseup() { // Make sure that clear-selection is released. // Otherwise selection using Shift key may be broken.
gBrowser.unlockClearMultiSelection();
this.style.MozUserFocus = "";
}
on_click(event) { if (event.button != 0) { return;
}
if (event.getModifierState("Accel") || event.shiftKey) { return;
}
if (
gBrowser.multiSelectedTabsCount > 0 &&
!event.target.classList.contains("tab-close-button") &&
!event.target.classList.contains("tab-icon-overlay") &&
!event.target.classList.contains("tab-audio-button")
) { // Tabs were previously multi-selected and user clicks on a tab // without holding Ctrl/Cmd Key
gBrowser.clearMultiSelectedTabs();
}
if (
event.target.classList.contains("tab-icon-overlay") ||
event.target.classList.contains("tab-audio-button")
) { if (this.activeMediaBlocked) { if (this.multiselected) {
gBrowser.resumeDelayedMediaOnMultiSelectedTabs(this);
} else { this.resumeDelayedMedia();
}
} elseif (this.soundPlaying || this.muted) { if (this.multiselected) {
gBrowser.toggleMuteAudioOnMultiSelectedTabs(this);
} else { this.toggleMuteAudio();
}
} return;
}
if (event.target.classList.contains("tab-close-button")) { if (this.multiselected) {
gBrowser.removeMultiSelectedTabs(
lazy.TabMetrics.userTriggeredContext(
lazy.TabMetrics.METRIC_SOURCE.TAB_STRIP
)
);
} else {
gBrowser.removeTab(this, {
animate: true,
triggeringEvent: event,
...lazy.TabMetrics.userTriggeredContext(
lazy.TabMetrics.METRIC_SOURCE.TAB_STRIP
),
});
} // This enables double-click protection for the tab container // (see tabbrowser-tabs 'click' handler).
gBrowser.tabContainer._blockDblClick = true;
}
}
on_dblclick(event) { if (event.button != 0) { return;
}
// for the one-close-button case if (event.target.classList.contains("tab-close-button")) {
event.stopPropagation();
}
on_animationstart(event) { if (!event.animationName.startsWith("tab-throbber-animation")) { return;
} // The animation is on a pseudo-element so we need to use `subtree: true` // to get our hands on it. for (let animation of event.target.getAnimations({ subtree: true })) { if (animation.animationName === event.animationName) { // Ensure all tab throbber animations are synchronized by sharing an // start time.
animation.startTime = 0;
}
}
}
on_animationend(event) { if (event.target.classList.contains("tab-loading-burst")) { this.removeAttribute("bursting");
}
}
_mouseenter() { this._hover = true;
if (this.selected) { this.container._handleTabSelect();
} elseif (this.linkedPanel) { this.linkedBrowser.unselectedTabHover(true);
}
// Prepare connection to host beforehand.
SessionStore.speculativeConnectOnTabHover(this);
toggleMuteAudio(aMuteReason) {
let browser = this.linkedBrowser; if (browser.audioMuted) { if (this.linkedPanel) { // "Lazy Browser" should not invoke its unmute method
browser.unmute();
} this.removeAttribute("muted");
} else { if (this.linkedPanel) { // "Lazy Browser" should not invoke its mute method
browser.mute();
} this.toggleAttribute("muted", true);
} this.muteReason = aMuteReason || null;
gBrowser._tabAttrModified(this, ["muted"]);
}
setUserContextId(aUserContextId) { if (aUserContextId) { if (this.linkedBrowser) { this.linkedBrowser.setAttribute("usercontextid", aUserContextId);
} this.setAttribute("usercontextid", aUserContextId);
} else { if (this.linkedBrowser) { this.linkedBrowser.removeAttribute("usercontextid");
} this.removeAttribute("usercontextid");
}
ContextualIdentityService.setTabStyle(this);
}
updateA11yDescription() {
let prevDescTab = gBrowser.tabContainer.querySelector( "tab[aria-describedby]"
); if (prevDescTab) { // We can only have a description for the focused tab.
prevDescTab.removeAttribute("aria-describedby");
}
let desc = document.getElementById("tabbrowser-tab-a11y-desc");
desc.textContent = gBrowser.getTabTooltip(this, false); this.setAttribute("aria-describedby", "tabbrowser-tab-a11y-desc");
}
#updateOnTabGrouped() { if (this.group && this.#lastGroup != this.group) { // Trigger TabGrouped on the tab group, not the tab itself. This is a // bit unorthodox, but fixes bug1964152 where tab group events are not // fired correctly when tabs change windows (because the tab is // detached from the DOM at time of the event). this.group.dispatchEvent( new CustomEvent("TabGrouped", {
bubbles: true,
detail: this,
})
); this.setAttribute("aria-level", 2);
}
}
#updateOnTabUngrouped() { if (this.#lastGroup && this.#lastGroup != this.group) { // Trigger TabUngrouped on the tab group, not the tab itself. This is a // bit unorthodox, but fixes bug1964152 where tab group events are not // fired correctly when tabs change windows (because the tab is // detached from the DOM at time of the event). this.#lastGroup.dispatchEvent( new CustomEvent("TabUngrouped", {
bubbles: true,
detail: this,
})
); // Tab could have moved to be ungrouped (level 1) // or to a different group (level 2). this.setAttribute("aria-level", this.group ? 2 : 1); // `posinset` and `setsize` only need to be set explicitly // on grouped tabs so that a11y tools can tell users that a // given tab is "2 of 7" in the group, for example. this.removeAttribute("aria-posinset"); this.removeAttribute("aria-setsize");
}
}
#updateOnTabSplit() { if (this.splitview) { this.setAttribute("aria-level", 2);
}
}
#updateOnTabUnsplit() { if (!this.splitview) { this.setAttribute("aria-level", 1); // `posinset` and `setsize` only need to be set explicitly // on split view tabs so that a11y tools can tell users that a // given tab is "1 of 2" in the split view, for example. this.removeAttribute("aria-posinset"); this.removeAttribute("aria-setsize"); this.removeAttribute("aria-label");
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.