forked from mirrors/gecko-dev
		
	 f1d137eff5
			
		
	
	
		f1d137eff5
		
	
	
	
	
		
			
			A bunch of existing code assumes that <tab> elements are the immediate and only children of a <tabs> element, and uses dom apis to traverse relationships between these elements. To simplify conversion of <tabs> to a custom element (and hopefully improve readability a bit at the same time!), introduce new apis: On <tab> this.parentNode -> this.container this.nextElementSibling -> this.container.findNextTab(...) this.previousElementSibiling -> this.container.findNextTab(...) On <tabs> this.children -> this.allTabs Differential Revision: https://phabricator.services.mozilla.com/D34648 --HG-- extra : source : f4e21e465f384b90fa1e768141c4db708748bf66 extra : histedit_source : 95d8a4242e8e04df9e29c2b647558d37e910b845
		
			
				
	
	
		
			580 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			580 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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 is loaded into chrome windows with the subscript loader. Wrap in
 | |
| // a block to prevent accidentally leaking globals onto `window`.
 | |
| {
 | |
| class MozTabbrowserTab extends MozElements.MozTab {
 | |
|   constructor() {
 | |
|     super();
 | |
| 
 | |
|     this.addEventListener("mouseover", this);
 | |
|     this.addEventListener("mouseout", this);
 | |
|     this.addEventListener("dragstart", this, true);
 | |
|     this.addEventListener("dragstart", this);
 | |
|     this.addEventListener("mousedown", this);
 | |
|     this.addEventListener("mouseup", this);
 | |
|     this.addEventListener("click", this);
 | |
|     this.addEventListener("dblclick", this, true);
 | |
|     this.addEventListener("animationend", this);
 | |
| 
 | |
|     this._selectedOnFirstMouseDown = false;
 | |
| 
 | |
|     /**
 | |
|      * Describes how the tab ended up in this mute state. May be any of:
 | |
|      *
 | |
|      * - undefined: The tabs mute state has never changed.
 | |
|      * - null: The mute state was last changed through the UI.
 | |
|      * - Any string: The ID was changed through an extension API. The string
 | |
|      * must be the ID of the extension which changed it.
 | |
|      */
 | |
|     this.muteReason = undefined;
 | |
| 
 | |
|     this.mOverCloseButton = false;
 | |
| 
 | |
|     this.mCorrespondingMenuitem = null;
 | |
| 
 | |
|     this.closing = false;
 | |
|   }
 | |
| 
 | |
|   static get inheritedAttributes() {
 | |
|     return {
 | |
|       ".tab-background": "selected=visuallyselected,fadein,multiselected",
 | |
|       ".tab-line": "selected=visuallyselected,multiselected,before-multiselected",
 | |
|       ".tab-loading-burst": "pinned,bursting,notselectedsinceload",
 | |
|       ".tab-content": "pinned,selected=visuallyselected,titlechanged,attention",
 | |
|       ".tab-throbber": "fadein,pinned,busy,progress,selected=visuallyselected",
 | |
|       ".tab-icon-pending": "fadein,pinned,busy,progress,selected=visuallyselected,pendingicon",
 | |
|       ".tab-icon-image": "src=image,triggeringprincipal=iconloadingprincipal,requestcontextid,fadein,pinned,selected=visuallyselected,busy,crashed,sharing",
 | |
|       ".tab-sharing-icon-overlay": "sharing,selected=visuallyselected,pinned",
 | |
|       ".tab-icon-overlay": "crashed,busy,soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected,activemedia-blocked",
 | |
|       ".tab-label-container": "pinned,selected=visuallyselected,labeldirection",
 | |
|       ".tab-label": "text=label,accesskey,fadein,pinned,selected=visuallyselected,attention",
 | |
|       ".tab-icon-sound": "soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected,activemedia-blocked,pictureinpicture",
 | |
|       ".tab-close-button": "fadein,pinned,selected=visuallyselected",
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   get fragment() {
 | |
|     if (!this._fragment) {
 | |
|       this._fragment = MozXULElement.parseXULToFragment(`
 | |
|         <stack class="tab-stack" flex="1">
 | |
|           <vbox class="tab-background">
 | |
|             <hbox class="tab-line"/>
 | |
|             <spacer flex="1" class="tab-background-inner"/>
 | |
|             <hbox class="tab-bottom-line"/>
 | |
|           </vbox>
 | |
|           <hbox class="tab-loading-burst"/>
 | |
|           <hbox class="tab-content" align="center">
 | |
|             <hbox class="tab-throbber" layer="true"/>
 | |
|             <hbox class="tab-icon-pending"/>
 | |
|             <image class="tab-icon-image" validate="never" role="presentation"/>
 | |
|             <image class="tab-sharing-icon-overlay" role="presentation"/>
 | |
|             <image class="tab-icon-overlay" role="presentation"/>
 | |
|             <hbox class="tab-label-container"
 | |
|                   onoverflow="this.setAttribute('textoverflow', 'true');"
 | |
|                   onunderflow="this.removeAttribute('textoverflow');"
 | |
|                   flex="1">
 | |
|               <label class="tab-text tab-label" role="presentation"/>
 | |
|             </hbox>
 | |
|             <image class="tab-icon-sound" role="presentation"/>
 | |
|             <image class="tab-close-button close-icon" role="presentation"/>
 | |
|           </hbox>
 | |
|         </stack>
 | |
|       `);
 | |
|     }
 | |
|     return this.ownerDocument.importNode(this._fragment, true);
 | |
|   }
 | |
| 
 | |
|   connectedCallback() {
 | |
|     if (this._initialized) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     this.textContent = "";
 | |
|     this.appendChild(this.fragment);
 | |
|     this.initializeAttributeInheritance();
 | |
|     this.setAttribute("context", "tabContextMenu");
 | |
|     this._initialized = true;
 | |
| 
 | |
|     if (!("_lastAccessed" in this)) {
 | |
|       this.updateLastAccessed();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   get container() {
 | |
|     return gBrowser.tabContainer;
 | |
|   }
 | |
| 
 | |
|   set _visuallySelected(val) {
 | |
|     if (val == (this.getAttribute("visuallyselected") == "true")) {
 | |
|       return val;
 | |
|     }
 | |
| 
 | |
|     if (val) {
 | |
|       this.setAttribute("visuallyselected", "true");
 | |
|     } else {
 | |
|       this.removeAttribute("visuallyselected");
 | |
|     }
 | |
|     gBrowser._tabAttrModified(this, ["visuallyselected"]);
 | |
| 
 | |
|     return val;
 | |
|   }
 | |
| 
 | |
|   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 should update the visual selection as well at the same time,
 | |
|     // *or* if we're e10s and the visually selected tab isn't changing, in which case the
 | |
|     // tab switcher code won't run and update anything else (like the before- and after-
 | |
|     // selected attributes).
 | |
|     if (!gMultiProcessBrowser || (val && this.hasAttribute("visuallyselected"))) {
 | |
|       this._visuallySelected = val;
 | |
|     }
 | |
| 
 | |
|     return val;
 | |
|   }
 | |
| 
 | |
|   get pinned() {
 | |
|     return this.getAttribute("pinned") == "true";
 | |
|   }
 | |
| 
 | |
|   get hidden() {
 | |
|     return this.getAttribute("hidden") == "true";
 | |
|   }
 | |
| 
 | |
|   get muted() {
 | |
|     return this.getAttribute("muted") == "true";
 | |
|   }
 | |
| 
 | |
|   get multiselected() {
 | |
|     return this.getAttribute("multiselected") == "true";
 | |
|   }
 | |
| 
 | |
|   get beforeMultiselected() {
 | |
|     return this.getAttribute("before-multiselected") == "true";
 | |
|   }
 | |
| 
 | |
|   get userContextId() {
 | |
|     return this.hasAttribute("usercontextid") ?
 | |
|       parseInt(this.getAttribute("usercontextid")) :
 | |
|       0;
 | |
|   }
 | |
| 
 | |
|   get soundPlaying() {
 | |
|     return this.getAttribute("soundplaying") == "true";
 | |
|   }
 | |
| 
 | |
|   get pictureinpicture() {
 | |
|     return this.getAttribute("pictureinpicture") == "true";
 | |
|   }
 | |
| 
 | |
|   get activeMediaBlocked() {
 | |
|     return this.getAttribute("activemedia-blocked") == "true";
 | |
|   }
 | |
| 
 | |
|   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"))
 | |
|       return false;
 | |
| 
 | |
|     if (this.hasAttribute("customizemode"))
 | |
|       return false;
 | |
| 
 | |
|     let browser = this.linkedBrowser;
 | |
|     if (!isBlankPageURL(browser.currentURI.spec))
 | |
|       return false;
 | |
| 
 | |
|     if (!checkEmptyPageOrigin(browser))
 | |
|       return false;
 | |
| 
 | |
|     if (browser.canGoForward || browser.canGoBack)
 | |
|       return false;
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   get lastAccessed() {
 | |
|     return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
 | |
|   }
 | |
| 
 | |
|   get _overPlayingIcon() {
 | |
|     let iconVisible = this.hasAttribute("soundplaying") ||
 | |
|       this.hasAttribute("muted") ||
 | |
|       this.hasAttribute("activemedia-blocked");
 | |
| 
 | |
|     let soundPlayingIcon = this.soundPlayingIcon;
 | |
|     let overlayIcon = this.overlayIcon;
 | |
|     return soundPlayingIcon && soundPlayingIcon.matches(":hover") ||
 | |
|       (overlayIcon && overlayIcon.matches(":hover") && iconVisible);
 | |
|   }
 | |
| 
 | |
|   get soundPlayingIcon() {
 | |
|     return this.querySelector(".tab-icon-sound");
 | |
|   }
 | |
| 
 | |
|   get overlayIcon() {
 | |
|     return this.querySelector(".tab-icon-overlay");
 | |
|   }
 | |
| 
 | |
|   get throbber() {
 | |
|     return this.querySelector(".tab-throbber");
 | |
|   }
 | |
| 
 | |
|   get iconImage() {
 | |
|     return this.querySelector(".tab-icon-image");
 | |
|   }
 | |
| 
 | |
|   get sharingIcon() {
 | |
|     return this.querySelector(".tab-sharing-icon-overlay");
 | |
|   }
 | |
| 
 | |
|   get textLabel() {
 | |
|     return this.querySelector(".tab-label");
 | |
|   }
 | |
| 
 | |
|   get closeButton() {
 | |
|     return this.querySelector(".tab-close-button");
 | |
|   }
 | |
| 
 | |
|   updateLastAccessed(aDate) {
 | |
|     this._lastAccessed = this.selected ? Infinity : (aDate || Date.now());
 | |
|   }
 | |
| 
 | |
|   on_mouseover(event) {
 | |
|     if (event.target.classList.contains("tab-close-button")) {
 | |
|       this.mOverCloseButton = true;
 | |
|     }
 | |
|     this._mouseenter();
 | |
|   }
 | |
| 
 | |
|   on_mouseout(event) {
 | |
|     if (event.target.classList.contains("tab-close-button")) {
 | |
|       this.mOverCloseButton = false;
 | |
|     }
 | |
|     this._mouseleave();
 | |
|   }
 | |
| 
 | |
|   on_dragstart(event) {
 | |
|     if (event.eventPhase == Event.CAPTURING_PHASE) {
 | |
|       this.style.MozUserFocus = "";
 | |
|     } else if (this.mOverCloseButton) {
 | |
|       event.stopPropagation();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   on_mousedown(event) {
 | |
|     let eventMaySelectTab = true;
 | |
|     let tabContainer = this.container;
 | |
| 
 | |
|     if (tabContainer._closeTabByDblclick &&
 | |
|         event.button == 0 &&
 | |
|         event.detail == 1) {
 | |
|       this._selectedOnFirstMouseDown = this.selected;
 | |
|     }
 | |
| 
 | |
|     if (this.selected) {
 | |
|       this.style.MozUserFocus = "ignore";
 | |
|     } else if (event.target.classList.contains("tab-close-button") ||
 | |
|                event.target.classList.contains("tab-icon-sound") ||
 | |
|                event.target.classList.contains("tab-icon-overlay")) {
 | |
|       eventMaySelectTab = false;
 | |
|     }
 | |
| 
 | |
|     if (event.button == 1) {
 | |
|       gBrowser.warmupTab(gBrowser._findTabToBlurTo(this));
 | |
|     }
 | |
| 
 | |
|     if (event.button == 0 && tabContainer._multiselectEnabled) {
 | |
|       let shiftKey = event.shiftKey;
 | |
|       let accelKey = event.getModifierState("Accel");
 | |
|       if (shiftKey) {
 | |
|         eventMaySelectTab = false;
 | |
|         const lastSelectedTab = gBrowser.lastMultiSelectedTab;
 | |
|         if (!accelKey) {
 | |
|           gBrowser.selectedTab = lastSelectedTab;
 | |
| 
 | |
|           // Make sure selection is cleared when tab-switch doesn't happen.
 | |
|           gBrowser.clearMultiSelectedTabs(false);
 | |
|         }
 | |
|         gBrowser.addRangeToMultiSelectedTabs(lastSelectedTab, this);
 | |
|       } else if (accelKey) {
 | |
|         // Ctrl (Cmd for mac) key is pressed
 | |
|         eventMaySelectTab = false;
 | |
|         if (this.multiselected) {
 | |
|           gBrowser.removeFromMultiSelectedTabs(this, true);
 | |
|         } else if (this != gBrowser.selectedTab) {
 | |
|           gBrowser.addToMultiSelectedTabs(this, false);
 | |
|           gBrowser.lastMultiSelectedTab = this;
 | |
|         }
 | |
|       } else if (!this.selected && this.multiselected) {
 | |
|         gBrowser.lockClearMultiSelectionOnce();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (eventMaySelectTab) {
 | |
|       super.on_mousedown(event);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   on_mouseup(event) {
 | |
|     // 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-sound") &&
 | |
|         !event.target.classList.contains("tab-icon-overlay")) {
 | |
|       // 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 (event.target.classList.contains("tab-icon-sound") ||
 | |
|         (event.target.classList.contains("tab-icon-overlay") &&
 | |
|           (event.target.hasAttribute("soundplaying") ||
 | |
|             event.target.hasAttribute("muted") ||
 | |
|             event.target.hasAttribute("activemedia-blocked")))) {
 | |
|       if (this.multiselected) {
 | |
|         gBrowser.toggleMuteAudioOnMultiSelectedTabs(this);
 | |
|       } else {
 | |
|         this.toggleMuteAudio();
 | |
|       }
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (event.target.classList.contains("tab-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;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   on_dblclick(event) {
 | |
|     if (event.button != 0) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // for the one-close-button case
 | |
|     if (event.target.classList.contains("tab-close-button")) {
 | |
|       event.stopPropagation();
 | |
|     }
 | |
| 
 | |
|     let tabContainer = this.container;
 | |
|     if (tabContainer._closeTabByDblclick &&
 | |
|         this._selectedOnFirstMouseDown &&
 | |
|         this.selected &&
 | |
|         !(event.target.classList.contains("tab-icon-sound") ||
 | |
|           event.target.classList.contains("tab-icon-overlay"))) {
 | |
|       gBrowser.removeTab(this, {
 | |
|         animate: true,
 | |
|         byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE,
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   on_animationend(event) {
 | |
|     if (event.target.classList.contains("tab-loading-burst")) {
 | |
|       this.removeAttribute("bursting");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   _mouseenter() {
 | |
|     if (this.hidden || this.closing) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let tabContainer = this.container;
 | |
|     let visibleTabs = tabContainer._getVisibleTabs();
 | |
|     let tabIndex = visibleTabs.indexOf(this);
 | |
| 
 | |
|     if (this.selected) {
 | |
|       tabContainer._handleTabSelect();
 | |
|     }
 | |
| 
 | |
|     if (tabIndex == 0) {
 | |
|       tabContainer._beforeHoveredTab = null;
 | |
|     } else {
 | |
|       let candidate = visibleTabs[tabIndex - 1];
 | |
|       let separatedByScrollButton =
 | |
|         tabContainer.getAttribute("overflow") == "true" &&
 | |
|         candidate.pinned && !this.pinned;
 | |
|       if (!candidate.selected && !separatedByScrollButton) {
 | |
|         tabContainer._beforeHoveredTab = candidate;
 | |
|         candidate.setAttribute("beforehovered", "true");
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (tabIndex == visibleTabs.length - 1) {
 | |
|       tabContainer._afterHoveredTab = null;
 | |
|     } else {
 | |
|       let candidate = visibleTabs[tabIndex + 1];
 | |
|       if (!candidate.selected) {
 | |
|         tabContainer._afterHoveredTab = candidate;
 | |
|         candidate.setAttribute("afterhovered", "true");
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     tabContainer._hoveredTab = this;
 | |
|     if (this.linkedPanel && !this.selected) {
 | |
|       this.linkedBrowser.unselectedTabHover(true);
 | |
|       this.startUnselectedTabHoverTimer();
 | |
|     }
 | |
| 
 | |
|     // Prepare connection to host beforehand.
 | |
|     SessionStore.speculativeConnectOnTabHover(this);
 | |
| 
 | |
|     let tabToWarm = this;
 | |
|     if (this.mOverCloseButton) {
 | |
|       tabToWarm = gBrowser._findTabToBlurTo(this);
 | |
|     }
 | |
|     gBrowser.warmupTab(tabToWarm);
 | |
|   }
 | |
| 
 | |
|   _mouseleave() {
 | |
|     let tabContainer = this.container;
 | |
|     if (tabContainer._beforeHoveredTab) {
 | |
|       tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
 | |
|       tabContainer._beforeHoveredTab = null;
 | |
|     }
 | |
|     if (tabContainer._afterHoveredTab) {
 | |
|       tabContainer._afterHoveredTab.removeAttribute("afterhovered");
 | |
|       tabContainer._afterHoveredTab = null;
 | |
|     }
 | |
| 
 | |
|     tabContainer._hoveredTab = null;
 | |
|     if (this.linkedPanel && !this.selected) {
 | |
|       this.linkedBrowser.unselectedTabHover(false);
 | |
|       this.cancelUnselectedTabHoverTimer();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   startUnselectedTabHoverTimer() {
 | |
|     // Only record data when we need to.
 | |
|     if (!this.linkedBrowser.shouldHandleUnselectedTabHover) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (!TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
 | |
|       TelemetryStopwatch.start("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
 | |
|     }
 | |
| 
 | |
|     if (this._hoverTabTimer) {
 | |
|       clearTimeout(this._hoverTabTimer);
 | |
|       this._hoverTabTimer = null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   cancelUnselectedTabHoverTimer() {
 | |
|     // Since we're listening "mouseout" event, instead of "mouseleave".
 | |
|     // Every time the cursor is moving from the tab to its child node (icon),
 | |
|     // it would dispatch "mouseout"(for tab) first and then dispatch
 | |
|     // "mouseover" (for icon, eg: close button, speaker icon) soon.
 | |
|     // It causes we would cancel present TelemetryStopwatch immediately
 | |
|     // when cursor is moving on the icon, and then start a new one.
 | |
|     // In order to avoid this situation, we could delay cancellation and
 | |
|     // remove it if we get "mouseover" within very short period.
 | |
|     this._hoverTabTimer = setTimeout(() => {
 | |
|       if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
 | |
|         TelemetryStopwatch.cancel("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
 | |
|       }
 | |
|     }, 100);
 | |
|   }
 | |
| 
 | |
|   finishUnselectedTabHoverTimer() {
 | |
|     // Stop timer when the tab is opened.
 | |
|     if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
 | |
|       TelemetryStopwatch.finish("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   toggleMuteAudio(aMuteReason) {
 | |
|     let browser = this.linkedBrowser;
 | |
|     let modifiedAttrs = [];
 | |
|     let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
 | |
| 
 | |
|     if (this.hasAttribute("activemedia-blocked")) {
 | |
|       this.removeAttribute("activemedia-blocked");
 | |
|       modifiedAttrs.push("activemedia-blocked");
 | |
| 
 | |
|       browser.resumeMedia();
 | |
|       hist.add(3 /* unblockByClickingIcon */ );
 | |
|     } else {
 | |
|       if (browser.audioMuted) {
 | |
|         if (this.linkedPanel) {
 | |
|           // "Lazy Browser" should not invoke its unmute method
 | |
|           browser.unmute();
 | |
|         }
 | |
|         this.removeAttribute("muted");
 | |
|         hist.add(1 /* unmute */ );
 | |
|       } else {
 | |
|         if (this.linkedPanel) {
 | |
|           // "Lazy Browser" should not invoke its mute method
 | |
|           browser.mute();
 | |
|         }
 | |
|         this.setAttribute("muted", "true");
 | |
|         hist.add(0 /* mute */ );
 | |
|       }
 | |
|       this.muteReason = aMuteReason || null;
 | |
|       modifiedAttrs.push("muted");
 | |
|     }
 | |
|     gBrowser._tabAttrModified(this, modifiedAttrs);
 | |
|   }
 | |
| 
 | |
|   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);
 | |
|   }
 | |
| }
 | |
| 
 | |
| customElements.define("tabbrowser-tab", MozTabbrowserTab, {
 | |
|   extends: "tab",
 | |
| });
 | |
| }
 |