Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/browser/components/tabbrowser/content/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 276 kB image not shown  

Quelle  tabbrowser.js   Sprache: JAVA

 
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * 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/. */


/* eslint-env mozilla/browser-window */

{
  // start private scope for Tabbrowser
  /**
   * A set of known icons to use for internal pages. These are hardcoded so we can
   * start loading them faster than FaviconLoader would normally find them.
   */

  const FAVICON_DEFAULTS = {
    "about:newtab""chrome://branding/content/icon32.png",
    "about:home""chrome://branding/content/icon32.png",
    "about:welcome""chrome://branding/content/icon32.png",
    "about:privatebrowsing":
      "chrome://browser/skin/privatebrowsing/favicon.svg",
  };

  const {
    LOAD_FLAGS_NONE,
    LOAD_FLAGS_FROM_EXTERNAL,
    LOAD_FLAGS_FIRST_LOAD,
    LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL,
    LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
    LOAD_FLAGS_FIXUP_SCHEME_TYPOS,
    LOAD_FLAGS_FORCE_ALLOW_DATA_URI,
    LOAD_FLAGS_DISABLE_TRR,
  } = Ci.nsIWebNavigation;

  const DIRECTION_FORWARD = 1;
  const DIRECTION_BACKWARD = -1;

  /**
   * Updates the User Context UI indicators if the browser is in a non-default context
   */

  function updateUserContextUIIndicator() {
    function replaceContainerClass(classType, element, value) {
      let prefix = "identity-" + classType + "-";
      if (value && element.classList.contains(prefix + value)) {
        return;
      }
      for (let className of element.classList) {
        if (className.startsWith(prefix)) {
          element.classList.remove(className);
        }
      }
      if (value) {
        element.classList.add(prefix + value);
      }
    }

    let hbox = document.getElementById("userContext-icons");

    let userContextId = gBrowser.selectedBrowser.getAttribute("usercontextid");
    if (!userContextId) {
      replaceContainerClass("color", hbox, "");
      hbox.hidden = true;
      return;
    }

    let identity =
      ContextualIdentityService.getPublicIdentityFromId(userContextId);
    if (!identity) {
      replaceContainerClass("color", hbox, "");
      hbox.hidden = true;
      return;
    }

    replaceContainerClass("color", hbox, identity.color);

    let label = ContextualIdentityService.getUserContextLabel(userContextId);
    document.getElementById("userContext-label").textContent = label;
    // Also set the container label as the tooltip so we can only show the icon
    // in small windows.
    hbox.setAttribute("tooltiptext", label);

    let indicator = document.getElementById("userContext-indicator");
    replaceContainerClass("icon", indicator, identity.icon);

    hbox.hidden = false;
  }

  async function getTotalMemoryUsage() {
    const procInfo = await ChromeUtils.requestProcInfo();
    let totalMemoryUsage = procInfo.memory;
    for (const child of procInfo.children) {
      totalMemoryUsage += child.memory;
    }
    return totalMemoryUsage;
  }

  window.Tabbrowser = class {
    init() {
      this.tabContainer = document.getElementById("tabbrowser-tabs");
      this.tabGroupMenu = document.getElementById("tab-group-editor");
      this.tabbox = document.getElementById("tabbrowser-tabbox");
      this.tabpanels = document.getElementById("tabbrowser-tabpanels");
      this.verticalPinnedTabsContainer = document.getElementById(
        "vertical-pinned-tabs-container"
      );

      ChromeUtils.defineESModuleGetters(this, {
        AsyncTabSwitcher: "resource:///modules/AsyncTabSwitcher.sys.mjs",
        PictureInPicture: "resource://gre/modules/PictureInPicture.sys.mjs",
        UrlbarProviderOpenTabs:
          "resource:///modules/UrlbarProviderOpenTabs.sys.mjs",
      });
      XPCOMUtils.defineLazyServiceGetters(this, {
        MacSharingService: [
          "@mozilla.org/widget/macsharingservice;1",
          "nsIMacSharingService",
        ],
      });
      ChromeUtils.defineLazyGetter(this"tabLocalization", () => {
        return new Localization(
          ["browser/tabbrowser.ftl""branding/brand.ftl"],
          true
        );
      });
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "_shouldExposeContentTitle",
        "privacy.exposeContentTitleInWindow",
        true
      );
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "_shouldExposeContentTitlePbm",
        "privacy.exposeContentTitleInWindow.pbm",
        true
      );
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "_showTabCardPreview",
        "browser.tabs.hoverPreview.enabled",
        true
      );
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "_allowTransparentBrowser",
        "browser.tabs.allow_transparent_browser",
        false
      );
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "showPidAndActiveness",
        "browser.tabs.tooltipsShowPidAndActiveness",
        false
      );
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "_unloadTabInContextMenu",
        "browser.tabs.unloadTabInContextMenu",
        false
      );
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "_notificationEnableDelay",
        "security.notification_enable_delay",
        500
      );

      if (AppConstants.MOZ_CRASHREPORTER) {
        ChromeUtils.defineESModuleGetters(this, {
          TabCrashHandler: "resource:///modules/ContentCrashHandlers.sys.mjs",
        });
      }

      Services.obs.addObserver(this"contextual-identity-updated");

      document.addEventListener("keydown"this, { mozSystemGroup: true });
      document.addEventListener("keypress"this, { mozSystemGroup: true });
      document.addEventListener("visibilitychange"this);
      window.addEventListener("framefocusrequested"this);
      window.addEventListener("activate"this);
      window.addEventListener("deactivate"this);
      window.addEventListener("TabGroupCreate"this);

      this.tabContainer.init();
      this._setupInitialBrowserAndTab();

      if (
        Services.prefs.getIntPref("browser.display.document_color_use") == 2
      ) {
        this.tabpanels.style.backgroundColor = Services.prefs.getBoolPref(
          "browser.display.use_system_colors"
        )
          ? "canvas"
          : Services.prefs.getCharPref("browser.display.background_color");
      }

      this._setFindbarData();

      // We take over setting the document title, so remove the l10n id to
      // avoid it being re-translated and overwriting document content if
      // we ever switch languages at runtime. After a language change, the
      // window title will update at the next tab or location change.
      document.querySelector("title").removeAttribute("data-l10n-id");

      this._setupEventListeners();
      this._initialized = true;
    }

    ownerGlobal = window;

    ownerDocument = document;

    closingTabsEnum = {
      ALL: 0,
      OTHER: 1,
      TO_START: 2,
      TO_END: 3,
      MULTI_SELECTED: 4,
      DUPLICATES: 6,
      ALL_DUPLICATES: 7,
    };

    _lastRelatedTabMap = new WeakMap();

    mProgressListeners = [];

    mTabsProgressListeners = [];

    _tabListeners = new Map();

    _tabFilters = new Map();

    _isBusy = false;

    _awaitingToggleCaretBrowsingPrompt = false;

    _previewMode = false;

    _lastFindValue = "";

    _contentWaitingCount = 0;

    _tabLayerCache = [];

    tabAnimationsInProgress = 0;

    /**
     * Binding from browser to tab
     */

    _tabForBrowser = new WeakMap();

    /**
     * `_createLazyBrowser` will define properties on the unbound lazy browser
     * which correspond to properties defined in MozBrowser which will be bound to
     * the browser when it is inserted into the document.  If any of these
     * properties are accessed by consumers, `_insertBrowser` is called and
     * the browser is inserted to ensure that things don't break.  This list
     * provides the names of properties that may be called while the browser
     * is in its unbound (lazy) state.
     */

    _browserBindingProperties = [
      "canGoBack",
      "canGoForward",
      "goBack",
      "goForward",
      "permitUnload",
      "reload",
      "reloadWithFlags",
      "stop",
      "loadURI",
      "fixupAndLoadURIString",
      "gotoIndex",
      "currentURI",
      "documentURI",
      "remoteType",
      "preferences",
      "imageDocument",
      "isRemoteBrowser",
      "messageManager",
      "getTabBrowser",
      "finder",
      "fastFind",
      "sessionHistory",
      "contentTitle",
      "characterSet",
      "fullZoom",
      "textZoom",
      "tabHasCustomZoom",
      "webProgress",
      "addProgressListener",
      "removeProgressListener",
      "audioPlaybackStarted",
      "audioPlaybackStopped",
      "resumeMedia",
      "mute",
      "unmute",
      "blockedPopups",
      "lastURI",
      "purgeSessionHistory",
      "stopScroll",
      "startScroll",
      "userTypedValue",
      "userTypedClear",
      "didStartLoadSinceLastUserTyping",
      "audioMuted",
    ];

    _removingTabs = new Set();

    _multiSelectedTabsSet = new WeakSet();

    _lastMultiSelectedTabRef = null;

    _clearMultiSelectionLocked = false;

    _clearMultiSelectionLockedOnce = false;

    _multiSelectChangeStarted = false;

    _multiSelectChangeAdditions = new Set();

    _multiSelectChangeRemovals = new Set();

    _multiSelectChangeSelected = false;

    /**
     * Tab close requests are ignored if the window is closing anyway,
     * e.g. when holding Ctrl+W.
     */

    _windowIsClosing = false;

    preloadedBrowser = null;

    /**
     * This defines a proxy which allows us to access browsers by
     * index without actually creating a full array of browsers.
     */

    browsers = new Proxy([], {
      has: (target, name) => {
        if (typeof name == "string" && Number.isInteger(parseInt(name))) {
          return name in gBrowser.tabs;
        }
        return false;
      },
      get: (target, name) => {
        if (name == "length") {
          return gBrowser.tabs.length;
        }
        if (typeof name == "string" && Number.isInteger(parseInt(name))) {
          if (!(name in gBrowser.tabs)) {
            return undefined;
          }
          return gBrowser.tabs[name].linkedBrowser;
        }
        return target[name];
      },
    });

    /**
     * List of browsers whose docshells must be active in order for print preview
     * to work.
     */

    _printPreviewBrowsers = new Set();

    _switcher = null;

    _soundPlayingAttrRemovalTimer = 0;

    _hoverTabTimer = null;

    get _tabGroupsEnabled() {
      return NimbusFeatures.tabGroups.getVariable("enabled");
    }

    get tabs() {
      return this.tabContainer.allTabs;
    }

    get tabGroups() {
      return this.tabContainer.allGroups;
    }

    get tabsInCollapsedTabGroups() {
      return this.tabGroups
        .filter(tabGroup => tabGroup.collapsed)
        .flatMap(tabGroup => tabGroup.tabs)
        .filter(tab => !tab.hidden && !tab.isClosing);
    }

    addEventListener(...args) {
      this.tabpanels.addEventListener(...args);
    }

    removeEventListener(...args) {
      this.tabpanels.removeEventListener(...args);
    }

    dispatchEvent(...args) {
      return this.tabpanels.dispatchEvent(...args);
    }

    /**
     * Returns all tabs in the current window, including hidden tabs and tabs
     * in collapsed groups, but excluding closing tabs and the Firefox View tab.
     */

    get openTabs() {
      return this.tabContainer.openTabs;
    }

    /**
     * Same as `openTabs` but excluding hidden tabs and tabs in collapsed groups.
     */

    get visibleTabs() {
      return this.tabContainer.visibleTabs;
    }

    get pinnedTabCount() {
      for (var i = 0; i < this.tabs.length; i++) {
        if (!this.tabs[i].pinned) {
          break;
        }
      }
      return i;
    }

    set selectedTab(val) {
      if (
        gSharedTabWarning.willShowSharedTabWarning(val) ||
        document.documentElement.hasAttribute("window-modal-open") ||
        (gNavToolbox.collapsed && !this._allowTabChange)
      ) {
        return;
      }
      // Update the tab
      this.tabbox.selectedTab = val;
    }

    get selectedTab() {
      return this._selectedTab;
    }

    get selectedBrowser() {
      return this._selectedBrowser;
    }

    _setupInitialBrowserAndTab() {
      // See browser.js for the meaning of window.arguments.
      // Bug 1485961 covers making this more sane.
      let userContextId = window.arguments && window.arguments[5];

      let openWindowInfo = window.docShell.treeOwner
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIAppWindow).initialOpenWindowInfo;

      if (!openWindowInfo && window.arguments && window.arguments[11]) {
        openWindowInfo = window.arguments[11];
      }

      let tabArgument = gBrowserInit.getTabToAdopt();

      // If we have a tab argument with browser, we use its remoteType. Otherwise,
      // if e10s is disabled or there's a parent process opener (e.g. parent
      // process about: page) for the content tab, we use a parent
      // process remoteType. Otherwise, we check the URI to determine
      // what to do - if there isn't one, we default to the default remote type.
      //
      // When adopting a tab, we'll also use that tab's browsingContextGroupId,
      // if available, to ensure we don't spawn a new process.
      let remoteType;
      let initialBrowsingContextGroupId;

      if (tabArgument && tabArgument.hasAttribute("usercontextid")) {
        // The window's first argument is a tab if and only if we are swapping tabs.
        // We must set the browser's usercontextid so that the newly created remote
        // tab child has the correct usercontextid.
        userContextId = parseInt(tabArgument.getAttribute("usercontextid"), 10);
      }

      if (tabArgument && tabArgument.linkedBrowser) {
        remoteType = tabArgument.linkedBrowser.remoteType;
        initialBrowsingContextGroupId =
          tabArgument.linkedBrowser.browsingContext?.group.id;
      } else if (openWindowInfo) {
        userContextId = openWindowInfo.originAttributes.userContextId;
        if (openWindowInfo.isRemote) {
          remoteType = E10SUtils.DEFAULT_REMOTE_TYPE;
        } else {
          remoteType = E10SUtils.NOT_REMOTE;
        }
      } else {
        let uriToLoad = gBrowserInit.uriToLoadPromise;
        if (uriToLoad && Array.isArray(uriToLoad)) {
          uriToLoad = uriToLoad[0]; // we only care about the first item
        }

        if (uriToLoad && typeof uriToLoad == "string") {
          let oa = E10SUtils.predictOriginAttributes({
            window,
            userContextId,
          });
          remoteType = E10SUtils.getRemoteTypeForURI(
            uriToLoad,
            gMultiProcessBrowser,
            gFissionBrowser,
            E10SUtils.DEFAULT_REMOTE_TYPE,
            null,
            oa
          );
        } else {
          // If we reach here, we don't have the url to load. This means that
          // `uriToLoad` is most likely a promise which is waiting on SessionStore
          // initialization. We can't delay setting up the browser here, as that
          // would mean that `gBrowser.selectedBrowser` might not always exist,
          // which is the current assumption.

          // In this case we default to the privileged about process as that's
          // the best guess we can make, and we'll likely need it eventually.
          remoteType = E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE;
        }
      }

      let createOptions = {
        uriIsAboutBlank: false,
        userContextId,
        initialBrowsingContextGroupId,
        remoteType,
        openWindowInfo,
      };
      let browser = this.createBrowser(createOptions);
      browser.setAttribute("primary""true");
      if (gBrowserAllowScriptsToCloseInitialTabs) {
        browser.setAttribute("allowscriptstoclose""true");
      }
      browser.droppedLinkHandler = handleDroppedLink;
      browser.loadURI = URILoadingWrapper.loadURI.bind(
        URILoadingWrapper,
        browser
      );
      browser.fixupAndLoadURIString =
        URILoadingWrapper.fixupAndLoadURIString.bind(
          URILoadingWrapper,
          browser
        );

      let uniqueId = this._generateUniquePanelID();
      let panel = this.getPanel(browser);
      panel.id = uniqueId;
      this.tabpanels.appendChild(panel);

      let tab = this.tabs[0];
      tab.linkedPanel = uniqueId;
      this._selectedTab = tab;
      this._selectedBrowser = browser;
      tab.permanentKey = browser.permanentKey;
      tab._tPos = 0;
      tab._fullyOpen = true;
      tab.linkedBrowser = browser;

      if (userContextId) {
        tab.setAttribute("usercontextid", userContextId);
        ContextualIdentityService.setTabStyle(tab);
      }

      this._tabForBrowser.set(browser, tab);

      this._appendStatusPanel();

      // This is the initial browser, so it's usually active; the default is false
      // so we have to update it:
      browser.docShellIsActive = this.shouldActivateDocShell(browser);

      // Hook the browser up with a progress listener.
      let tabListener = new TabProgressListener(tab, browser, truefalse);
      let filter = Cc[
        "@mozilla.org/appshell/component/browser-status-filter;1"
      ].createInstance(Ci.nsIWebProgress);
      filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
      this._tabListeners.set(tab, tabListener);
      this._tabFilters.set(tab, filter);
      browser.webProgress.addProgressListener(
        filter,
        Ci.nsIWebProgress.NOTIFY_ALL
      );
    }

    /**
     * BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
     * MAKE SURE TO ADD IT HERE AS WELL.
     */

    get canGoBack() {
      return this.selectedBrowser.canGoBack;
    }

    get canGoBackIgnoringUserInteraction() {
      return this.selectedBrowser.canGoBackIgnoringUserInteraction;
    }

    get canGoForward() {
      return this.selectedBrowser.canGoForward;
    }

    goBack(requireUserInteraction) {
      return this.selectedBrowser.goBack(requireUserInteraction);
    }

    goForward(requireUserInteraction) {
      return this.selectedBrowser.goForward(requireUserInteraction);
    }

    reload() {
      return this.selectedBrowser.reload();
    }

    reloadWithFlags(aFlags) {
      return this.selectedBrowser.reloadWithFlags(aFlags);
    }

    stop() {
      return this.selectedBrowser.stop();
    }

    /**
     * throws exception for unknown schemes
     */

    loadURI(uri, params) {
      return this.selectedBrowser.loadURI(uri, params);
    }
    /**
     * throws exception for unknown schemes
     */

    fixupAndLoadURIString(uriString, params) {
      return this.selectedBrowser.fixupAndLoadURIString(uriString, params);
    }

    gotoIndex(aIndex) {
      return this.selectedBrowser.gotoIndex(aIndex);
    }

    get currentURI() {
      return this.selectedBrowser.currentURI;
    }

    get finder() {
      return this.selectedBrowser.finder;
    }

    get docShell() {
      return this.selectedBrowser.docShell;
    }

    get webNavigation() {
      return this.selectedBrowser.webNavigation;
    }

    get webProgress() {
      return this.selectedBrowser.webProgress;
    }

    get contentWindow() {
      return this.selectedBrowser.contentWindow;
    }

    get sessionHistory() {
      return this.selectedBrowser.sessionHistory;
    }

    get contentDocument() {
      return this.selectedBrowser.contentDocument;
    }

    get contentTitle() {
      return this.selectedBrowser.contentTitle;
    }

    get contentPrincipal() {
      return this.selectedBrowser.contentPrincipal;
    }

    get securityUI() {
      return this.selectedBrowser.securityUI;
    }

    set fullZoom(val) {
      this.selectedBrowser.fullZoom = val;
    }

    get fullZoom() {
      return this.selectedBrowser.fullZoom;
    }

    set textZoom(val) {
      this.selectedBrowser.textZoom = val;
    }

    get textZoom() {
      return this.selectedBrowser.textZoom;
    }

    get isSyntheticDocument() {
      return this.selectedBrowser.isSyntheticDocument;
    }

    set userTypedValue(val) {
      this.selectedBrowser.userTypedValue = val;
    }

    get userTypedValue() {
      return this.selectedBrowser.userTypedValue;
    }

    _setFindbarData() {
      // Ensure we know what the find bar key is in the content process:
      let { sharedData } = Services.ppmm;
      if (!sharedData.has("Findbar:Shortcut")) {
        let keyEl = document.getElementById("key_find");
        let mods = keyEl
          .getAttribute("modifiers")
          .replace(
            /accel/i,
            AppConstants.platform == "macosx" ? "meta" : "control"
          );
        sharedData.set("Findbar:Shortcut", {
          key: keyEl.getAttribute("key"),
          shiftKey: mods.includes("shift"),
          ctrlKey: mods.includes("control"),
          altKey: mods.includes("alt"),
          metaKey: mods.includes("meta"),
        });
      }
    }

    isFindBarInitialized(aTab) {
      return (aTab || this.selectedTab)._findBar != undefined;
    }

    /**
     * Get the already constructed findbar
     */

    getCachedFindBar(aTab = this.selectedTab) {
      return aTab._findBar;
    }

    /**
     * Get the findbar, and create it if it doesn't exist.
     * @return the find bar (or null if the window or tab is closed/closing in the interim).
     */

    async getFindBar(aTab = this.selectedTab) {
      let findBar = this.getCachedFindBar(aTab);
      if (findBar) {
        return findBar;
      }

      // Avoid re-entrancy by caching the promise we're about to return.
      if (!aTab._pendingFindBar) {
        aTab._pendingFindBar = this._createFindBar(aTab);
      }
      return aTab._pendingFindBar;
    }

    /**
     * Create a findbar instance.
     * @param aTab the tab to create the find bar for.
     * @return the created findbar, or null if the window or tab is closed/closing.
     */

    async _createFindBar(aTab) {
      let findBar = document.createXULElement("findbar");
      let browser = this.getBrowserForTab(aTab);

      browser.parentNode.insertAdjacentElement("afterend", findBar);

      await new Promise(r => requestAnimationFrame(r));
      delete aTab._pendingFindBar;
      if (window.closed || aTab.closing) {
        return null;
      }

      findBar.browser = browser;
      findBar._findField.value = this._lastFindValue;

      aTab._findBar = findBar;

      let event = document.createEvent("Events");
      event.initEvent("TabFindInitialized"truefalse);
      aTab.dispatchEvent(event);

      return findBar;
    }

    _appendStatusPanel() {
      this.selectedBrowser.insertAdjacentElement("afterend", StatusPanel.panel);
    }

    _updateTabBarForPinnedTabs() {
      this.tabContainer._unlockTabSizing();
      this.tabContainer._positionPinnedTabs();
      this.tabContainer._updateCloseButtons();
    }

    _notifyPinnedStatus(aTab) {
      // browsingContext is expected to not be defined on discarded tabs.
      if (aTab.linkedBrowser.browsingContext) {
        aTab.linkedBrowser.browsingContext.isAppTab = aTab.pinned;
      }

      let event = document.createEvent("Events");
      event.initEvent(aTab.pinned ? "TabPinned" : "TabUnpinned"truefalse);
      aTab.dispatchEvent(event);
    }

    pinTab(aTab) {
      if (aTab.pinned) {
        return;
      }

      this.showTab(aTab);
      this.ungroupTab(aTab);
      if (this.tabContainer.verticalMode) {
        this._handleTabMove(aTab, () =>
          this.verticalPinnedTabsContainer.appendChild(aTab)
        );
      } else {
        this.moveTabTo(aTab, this.pinnedTabCount, { forceStandaloneTab: true });
      }
      aTab.setAttribute("pinned""true");
      this._updateTabBarForPinnedTabs();
      this._notifyPinnedStatus(aTab);
    }

    unpinTab(aTab) {
      if (!aTab.pinned) {
        return;
      }

      if (this.tabContainer.verticalMode) {
        this._handleTabMove(aTab, () => {
          // we remove this attribute first, so that allTabs represents
          // the moving of a tab from the vertical pinned tabs container
          // and back into arrowscrollbox.
          aTab.removeAttribute("pinned");
          this.tabContainer.arrowScrollbox.prepend(aTab);
        });
      } else {
        this.moveTabTo(aTab, this.pinnedTabCount - 1, {
          forceStandaloneTab: true,
        });
        aTab.removeAttribute("pinned");
      }
      aTab.style.marginInlineStart = "";
      aTab._pinnedUnscrollable = false;
      this._updateTabBarForPinnedTabs();
      this._notifyPinnedStatus(aTab);
    }

    previewTab(aTab, aCallback) {
      let currentTab = this.selectedTab;
      try {
        // Suppress focus, ownership and selected tab changes
        this._previewMode = true;
        this.selectedTab = aTab;
        aCallback();
      } finally {
        this.selectedTab = currentTab;
        this._previewMode = false;
      }
    }

    getBrowserAtIndex(aIndex) {
      return this.browsers[aIndex];
    }

    getBrowserForOuterWindowID(aID) {
      for (let b of this.browsers) {
        if (b.outerWindowID == aID) {
          return b;
        }
      }

      return null;
    }

    getTabForBrowser(aBrowser) {
      return this._tabForBrowser.get(aBrowser);
    }

    getPanel(aBrowser) {
      return this.getBrowserContainer(aBrowser).parentNode;
    }

    getBrowserContainer(aBrowser) {
      return (aBrowser || this.selectedBrowser).parentNode.parentNode;
    }

    getTabNotificationDeck() {
      if (!this._tabNotificationDeck) {
        let template = document.getElementById(
          "tab-notification-deck-template"
        );
        template.replaceWith(template.content);
        this._tabNotificationDeck = document.getElementById(
          "tab-notification-deck"
        );
      }
      return this._tabNotificationDeck;
    }

    _nextNotificationBoxId = 0;
    getNotificationBox(aBrowser) {
      let browser = aBrowser || this.selectedBrowser;
      if (!browser._notificationBox) {
        browser._notificationBox = new MozElements.NotificationBox(element => {
          element.setAttribute("notificationside""top");
          element.setAttribute(
            "name",
            `tab-notification-box-${this._nextNotificationBoxId++}`
          );
          this.getTabNotificationDeck().append(element);
          if (browser == this.selectedBrowser) {
            this._updateVisibleNotificationBox(browser);
          }
        }, this._notificationEnableDelay);
      }
      return browser._notificationBox;
    }

    readNotificationBox(aBrowser) {
      let browser = aBrowser || this.selectedBrowser;
      return browser._notificationBox || null;
    }

    _updateVisibleNotificationBox(aBrowser) {
      if (!this._tabNotificationDeck) {
        // If the deck hasn't been created we don't need to create it here.
        return;
      }
      let notificationBox = this.readNotificationBox(aBrowser);
      this.getTabNotificationDeck().selectedViewName = notificationBox
        ? notificationBox.stack.getAttribute("name")
        : "";
    }

    getTabDialogBox(aBrowser) {
      if (!aBrowser) {
        throw new Error("aBrowser is required");
      }
      if (!aBrowser.tabDialogBox) {
        aBrowser.tabDialogBox = new TabDialogBox(aBrowser);
      }
      return aBrowser.tabDialogBox;
    }

    getTabFromAudioEvent(aEvent) {
      if (!aEvent.isTrusted) {
        return null;
      }

      var browser = aEvent.originalTarget;
      var tab = this.getTabForBrowser(browser);
      return tab;
    }

    _callProgressListeners(
      aBrowser,
      aMethod,
      aArguments,
      aCallGlobalListeners = true,
      aCallTabsListeners = true
    ) {
      var rv = true;

      function callListeners(listeners, args) {
        for (let p of listeners) {
          if (aMethod in p) {
            try {
              if (!p[aMethod].apply(p, args)) {
                rv = false;
              }
            } catch (e) {
              // don't inhibit other listeners
              console.error(e);
            }
          }
        }
      }

      aBrowser = aBrowser || this.selectedBrowser;

      if (aCallGlobalListeners && aBrowser == this.selectedBrowser) {
        callListeners(this.mProgressListeners, aArguments);
      }

      if (aCallTabsListeners) {
        aArguments.unshift(aBrowser);

        callListeners(this.mTabsProgressListeners, aArguments);
      }

      return rv;
    }

    /**
     * Sets an icon for the tab if the URI is defined in FAVICON_DEFAULTS.
     */

    setDefaultIcon(aTab, aURI) {
      if (aURI && aURI.spec in FAVICON_DEFAULTS) {
        this.setIcon(aTab, FAVICON_DEFAULTS[aURI.spec]);
      }
    }

    setIcon(
      aTab,
      aIconURL = "",
      aOriginalURL = aIconURL,
      aLoadingPrincipal = null,
      aClearImageFirst = false
    ) {
      let makeString = url => (url instanceof Ci.nsIURI ? url.spec : url);

      aIconURL = makeString(aIconURL);
      aOriginalURL = makeString(aOriginalURL);

      let LOCAL_PROTOCOLS = ["chrome:""about:""resource:""data:"];

      if (
        aIconURL &&
        !aLoadingPrincipal &&
        !LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol))
      ) {
        console.error(
          `Attempt to set a remote URL ${aIconURL} as a tab icon without a loading principal.`
        );
        return;
      }

      let browser = this.getBrowserForTab(aTab);
      browser.mIconURL = aIconURL;

      if (aIconURL != aTab.getAttribute("image")) {
        if (aClearImageFirst) {
          aTab.removeAttribute("image");
        }
        if (aIconURL) {
          if (aLoadingPrincipal) {
            aTab.setAttribute("iconloadingprincipal", aLoadingPrincipal);
          } else {
            aTab.removeAttribute("iconloadingprincipal");
          }
          aTab.setAttribute("image", aIconURL);
        } else {
          aTab.removeAttribute("image");
          aTab.removeAttribute("iconloadingprincipal");
        }
        this._tabAttrModified(aTab, ["image"]);
      }

      // The aOriginalURL argument is currently only used by tests.
      this._callProgressListeners(browser, "onLinkIconAvailable", [
        aIconURL,
        aOriginalURL,
      ]);
    }

    getIcon(aTab) {
      let browser = aTab ? this.getBrowserForTab(aTab) : this.selectedBrowser;
      return browser.mIconURL;
    }

    setPageInfo(aURL, aDescription, aPreviewImage) {
      if (aURL) {
        let pageInfo = {
          url: aURL,
          description: aDescription,
          previewImageURL: aPreviewImage,
        };
        PlacesUtils.history.update(pageInfo).catch(console.error);
      }
    }

    getWindowTitleForBrowser(aBrowser) {
      let docElement = document.documentElement;
      let title = "";
      let dataSuffix =
        docElement.getAttribute("privatebrowsingmode") == "temporary"
          ? "Private"
          : "Default";

      if (
        SelectableProfileService?.isEnabled &&
        SelectableProfileService.currentProfile
      ) {
        dataSuffix += "WithProfile";
      }
      let defaultTitle = docElement.dataset["title" + dataSuffix].replace(
        "PROFILENAME",
        () => SelectableProfileService.currentProfile.name.replace(/\0/g, "")
      );

      if (
        !this._shouldExposeContentTitle ||
        (PrivateBrowsingUtils.isWindowPrivate(window) &&
          !this._shouldExposeContentTitlePbm)
      ) {
        return defaultTitle;
      }

      // If location bar is hidden and the URL type supports a host,
      // add the scheme and host to the title to prevent spoofing.
      // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239
      try {
        if (docElement.getAttribute("chromehidden").includes("location")) {
          const uri = Services.io.createExposableURI(aBrowser.currentURI);
          let prefix = uri.prePath;
          if (uri.scheme == "about") {
            prefix = uri.spec;
          } else if (uri.scheme == "moz-extension") {
            const ext = WebExtensionPolicy.getByHostname(uri.host);
            if (ext && ext.name) {
              let extensionLabel = document.getElementById(
                "urlbar-label-extension"
              );
              prefix = `${extensionLabel.value} (${ext.name})`;
            }
          }
          title = prefix + " - ";
        }
      } catch (e) {
        // ignored
      }

      if (docElement.hasAttribute("titlepreface")) {
        title += docElement.getAttribute("titlepreface");
      }

      let tab = this.getTabForBrowser(aBrowser);
      if (tab._labelIsContentTitle) {
        // Strip out any null bytes in the content title, since the
        // underlying widget implementations of nsWindow::SetTitle pass
        // null-terminated strings to system APIs.
        title += tab.getAttribute("label").replace(/\0/g, "");
      }

      if (title) {
        // We're using a function rather than just using `title` as the
        // new substring to avoid `$$`, `$'` etc. having a special
        // meaning to `replace`.
        // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_string_as_a_parameter
        // and the documentation for functions for more info about this.
        return docElement.dataset["contentTitle" + dataSuffix]
          .replace("CONTENTTITLE", () => title)
          .replace(
            "PROFILENAME",
            () =>
              SelectableProfileService?.currentProfile?.name.replace(
                /\0/g,
                ""
              ) ?? ""
          );
      }

      return defaultTitle;
    }

    updateTitlebar() {
      document.title = this.getWindowTitleForBrowser(this.selectedBrowser);
    }

    updateCurrentBrowser(aForceUpdate) {
      let newBrowser = this.getBrowserAtIndex(this.tabContainer.selectedIndex);
      if (this.selectedBrowser == newBrowser && !aForceUpdate) {
        return;
      }

      let oldBrowser = this.selectedBrowser;
      // Once the async switcher starts, it's unpredictable when it will touch
      // the address bar, thus we store its state immediately.
      gURLBar?.saveSelectionStateForBrowser(oldBrowser);

      let newTab = this.getTabForBrowser(newBrowser);

      if (!aForceUpdate) {
        TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");

        if (gMultiProcessBrowser) {
          this._asyncTabSwitching = true;
          this._getSwitcher().requestTab(newTab);
          this._asyncTabSwitching = false;
        }

        document.commandDispatcher.lock();
      }

      let oldTab = this.selectedTab;

      // Preview mode should not reset the owner
      if (!this._previewMode && !oldTab.selected) {
        oldTab.owner = null;
      }

      let lastRelatedTab = this._lastRelatedTabMap.get(oldTab);
      if (lastRelatedTab) {
        if (!lastRelatedTab.selected) {
          lastRelatedTab.owner = null;
        }
      }
      this._lastRelatedTabMap = new WeakMap();

      if (!gMultiProcessBrowser) {
        oldBrowser.removeAttribute("primary");
        oldBrowser.docShellIsActive = false;
        newBrowser.setAttribute("primary""true");
        newBrowser.docShellIsActive = !document.hidden;
      }

      this._selectedBrowser = newBrowser;
      this._selectedTab = newTab;
      this.showTab(newTab);

      this._appendStatusPanel();

      this._updateVisibleNotificationBox(newBrowser);

      let oldBrowserPopupsBlocked =
        oldBrowser.popupBlocker.getBlockedPopupCount();
      let newBrowserPopupsBlocked =
        newBrowser.popupBlocker.getBlockedPopupCount();
      if (oldBrowserPopupsBlocked != newBrowserPopupsBlocked) {
        newBrowser.popupBlocker.updateBlockedPopupsUI();
      }

      // Update the URL bar.
      let webProgress = newBrowser.webProgress;
      this._callProgressListeners(
        null,
        "onLocationChange",
        [webProgress, null, newBrowser.currentURI, 0, true],
        true,
        false
      );

      let securityUI = newBrowser.securityUI;
      if (securityUI) {
        this._callProgressListeners(
          null,
          "onSecurityChange",
          [webProgress, null, securityUI.state],
          true,
          false
        );
        // Include the true final argument to indicate that this event is
        // simulated (instead of being observed by the webProgressListener).
        this._callProgressListeners(
          null,
          "onContentBlockingEvent",
          [webProgress, null, newBrowser.getContentBlockingEvents(), true],
          true,
          false
        );
      }

      let listener = this._tabListeners.get(newTab);
      if (listener && listener.mStateFlags) {
        this._callProgressListeners(
          null,
          "onUpdateCurrentBrowser",
          [
            listener.mStateFlags,
            listener.mStatus,
            listener.mMessage,
            listener.mTotalProgress,
          ],
          true,
          false
        );
      }

      if (!this._previewMode) {
        newTab.recordTimeFromUnloadToReload();
        newTab.updateLastAccessed();
        oldTab.updateLastAccessed();
        // if this is the foreground window, update the last-seen timestamps.
        if (this.ownerGlobal == BrowserWindowTracker.getTopWindow()) {
          newTab.updateLastSeenActive();
          oldTab.updateLastSeenActive();
        }

        let oldFindBar = oldTab._findBar;
        if (
          oldFindBar &&
          oldFindBar.findMode == oldFindBar.FIND_NORMAL &&
          !oldFindBar.hidden
        ) {
          this._lastFindValue = oldFindBar._findField.value;
        }

        this.updateTitlebar();

        newTab.removeAttribute("titlechanged");
        newTab.attention = false;

        // The tab has been selected, it's not unselected anymore.
        // Call the current browser's unselectedTabHover() with false
        // to dispatch an event.
        newBrowser.unselectedTabHover(false);
      }

      // If the new tab is busy, and our current state is not busy, then
      // we need to fire a start to all progress listeners.
      if (newTab.hasAttribute("busy") && !this._isBusy) {
        this._isBusy = true;
        this._callProgressListeners(
          null,
          "onStateChange",
          [
            webProgress,
            null,
            Ci.nsIWebProgressListener.STATE_START |
              Ci.nsIWebProgressListener.STATE_IS_NETWORK,
            0,
          ],
          true,
          false
        );
      }

      // If the new tab is not busy, and our current state is busy, then
      // we need to fire a stop to all progress listeners.
      if (!newTab.hasAttribute("busy") && this._isBusy) {
        this._isBusy = false;
        this._callProgressListeners(
          null,
          "onStateChange",
          [
            webProgress,
            null,
            Ci.nsIWebProgressListener.STATE_STOP |
              Ci.nsIWebProgressListener.STATE_IS_NETWORK,
            0,
          ],
          true,
          false
        );
      }

      // TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code
      // that might rely upon the other changes suppressed.
      // Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
      if (!this._previewMode) {
        // We've selected the new tab, so go ahead and notify listeners.
        let event = new CustomEvent("TabSelect", {
          bubbles: true,
          cancelable: false,
          detail: {
            previousTab: oldTab,
          },
        });
        newTab.dispatchEvent(event);

        this._tabAttrModified(oldTab, ["selected"]);
        this._tabAttrModified(newTab, ["selected"]);

        this._startMultiSelectChange();
        this._multiSelectChangeSelected = true;
        this.clearMultiSelectedTabs();
        if (this._multiSelectChangeAdditions.size) {
          // Some tab has been multiselected just before switching tabs.
          // The tab that was selected at that point should also be multiselected.
          this.addToMultiSelectedTabs(oldTab);
        }

        if (!gMultiProcessBrowser) {
          this._adjustFocusBeforeTabSwitch(oldTab, newTab);
          this._adjustFocusAfterTabSwitch(newTab);
        }

        // Bug 1781806 - A forced update can indicate the tab was already
        // selected. To ensure the internal state of the Urlbar is kept in
        // sync, notify it as if focus changed. Alternatively, if there is no
        // force update but the load context is not using remote tabs, there
        // can be a focus change due to the _adjustFocus above.
        if (aForceUpdate || !gMultiProcessBrowser) {
          gURLBar.afterTabSwitchFocusChange();
        }
      }

      updateUserContextUIIndicator();
      gPermissionPanel.updateSharingIndicator();

      // Enable touch events to start a native dragging
      // session to allow the user to easily drag the selected tab.
      // This is currently only supported on Windows.
      oldTab.removeAttribute("touchdownstartsdrag");
      newTab.setAttribute("touchdownstartsdrag""true");

      if (!gMultiProcessBrowser) {
        document.commandDispatcher.unlock();

        let event = new CustomEvent("TabSwitchDone", {
          bubbles: true,
          cancelable: true,
        });
        this.dispatchEvent(event);
      }

      if (!aForceUpdate) {
        TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
      }
    }

    _adjustFocusBeforeTabSwitch(oldTab, newTab) {
      if (this._previewMode) {
        return;
      }

      let oldBrowser = oldTab.linkedBrowser;
      let newBrowser = newTab.linkedBrowser;

      gURLBar.getBrowserState(oldBrowser).urlbarFocused = gURLBar.focused;

      if (this._asyncTabSwitching) {
        newBrowser._userTypedValueAtBeforeTabSwitch = newBrowser.userTypedValue;
      }

      if (this.isFindBarInitialized(oldTab)) {
        let findBar = this.getCachedFindBar(oldTab);
        oldTab._findBarFocused =
          !findBar.hidden &&
          findBar._findField.getAttribute("focused") == "true";
      }

      let activeEl = document.activeElement;
      // If focus is on the old tab, move it to the new tab.
      if (activeEl == oldTab) {
        newTab.focus();
      } else if (
        gMultiProcessBrowser &&
        activeEl != newBrowser &&
        activeEl != newTab
      ) {
        // In e10s, if focus isn't already in the tabstrip or on the new browser,
        // and the new browser's previous focus wasn't in the url bar but focus is
        // there now, we need to adjust focus further.
        let keepFocusOnUrlBar =
          newBrowser &&
          gURLBar.getBrowserState(newBrowser).urlbarFocused &&
          gURLBar.focused;
        if (!keepFocusOnUrlBar) {
          // Clear focus so that _adjustFocusAfterTabSwitch can detect if
          // some element has been focused and respect that.
          document.activeElement.blur();
        }
      }
    }

    _adjustFocusAfterTabSwitch(newTab) {
      // Don't steal focus from the tab bar.
      if (document.activeElement == newTab) {
        return;
      }

      let newBrowser = this.getBrowserForTab(newTab);

      if (newBrowser.hasAttribute("tabDialogShowing")) {
        newBrowser.tabDialogBox.focus();
        return;
      }
      // Focus the location bar if it was previously focused for that tab.
      // In full screen mode, only bother making the location bar visible
      // if the tab is a blank one.
      if (gURLBar.getBrowserState(newBrowser).urlbarFocused) {
        let selectURL = () => {
          if (this._asyncTabSwitching) {
            // Set _awaitingSetURI flag to suppress popup notification
            // explicitly while tab switching asynchronously.
            newBrowser._awaitingSetURI = true;

            // The onLocationChange event called in updateCurrentBrowser() will
            // be captured in browser.js, then it calls gURLBar.setURI(). In case
            // of that doing processing of here before doing above processing,
            // the selection status that gURLBar.select() does will be releasing
            // by gURLBar.setURI(). To resolve it, we call gURLBar.select() after
            // finishing gURLBar.setURI().
            const currentActiveElement = document.activeElement;
            gURLBar.inputField.addEventListener(
              "SetURI",
              () => {
                delete newBrowser._awaitingSetURI;

                // If the user happened to type into the URL bar for this browser
                // by the time we got here, focusing will cause the text to be
                // selected which could cause them to overwrite what they've
                // already typed in.
                let userTypedValueAtBeforeTabSwitch =
                  newBrowser._userTypedValueAtBeforeTabSwitch;
                delete newBrowser._userTypedValueAtBeforeTabSwitch;
                if (
                  newBrowser.userTypedValue &&
                  newBrowser.userTypedValue != userTypedValueAtBeforeTabSwitch
                ) {
                  return;
                }

                if (currentActiveElement != document.activeElement) {
                  return;
                }
                gURLBar.restoreSelectionStateForBrowser(newBrowser);
              },
              { once: true }
            );
          } else {
            gURLBar.restoreSelectionStateForBrowser(newBrowser);
          }
        };

        // This inDOMFullscreen attribute indicates that the page has something
        // such as a video in fullscreen mode. Opening a new tab will cancel
        // fullscreen mode, so we need to wait for that to happen and then
        // select the url field.
        if (window.document.documentElement.hasAttribute("inDOMFullscreen")) {
          window.addEventListener("MozDOMFullscreen:Exited", selectURL, {
            once: true,
            wantsUntrusted: false,
          });
          return;
        }

        if (!window.fullScreen || newTab.isEmpty) {
          selectURL();
          return;
        }
      }

      // Focus the find bar if it was previously focused for that tab.
      if (
        gFindBarInitialized &&
        !gFindBar.hidden &&
        this.selectedTab._findBarFocused
      ) {
        gFindBar._findField.focus();
        return;
      }

      // Don't focus the content area if something has been focused after the
      // tab switch was initiated.
      if (gMultiProcessBrowser && document.activeElement != document.body) {
        return;
      }

      // We're now committed to focusing the content area.
      let fm = Services.focus;
      let focusFlags = fm.FLAG_NOSCROLL;

      if (!gMultiProcessBrowser) {
        let newFocusedElement = fm.getFocusedElementForWindow(
          window.content,
          true,
          {}
        );

        // for anchors, use FLAG_SHOWRING so that it is clear what link was
        // last clicked when switching back to that tab
        if (
          newFocusedElement &&
          (HTMLAnchorElement.isInstance(newFocusedElement) ||
            newFocusedElement.getAttributeNS(
              "http://www.w3.org/1999/xlink",
              "type"
            ) == "simple")
        ) {
          focusFlags |= fm.FLAG_SHOWRING;
        }
      }

      fm.setFocus(newBrowser, focusFlags);
    }

    _tabAttrModified(aTab, aChanged) {
      if (aTab.closing) {
        return;
      }

      let event = new CustomEvent("TabAttrModified", {
        bubbles: true,
        cancelable: false,
        detail: {
          changed: aChanged,
        },
      });
      aTab.dispatchEvent(event);
    }

    resetBrowserSharing(aBrowser) {
      let tab = this.getTabForBrowser(aBrowser);
      if (!tab) {
        return;
      }
      // If WebRTC was used, leave object to enable tracking of grace periods.
      tab._sharingState = tab._sharingState?.webRTC ? { webRTC: {} } : {};
      tab.removeAttribute("sharing");
      this._tabAttrModified(tab, ["sharing"]);
      if (aBrowser == this.selectedBrowser) {
        gPermissionPanel.updateSharingIndicator();
      }
    }

    updateBrowserSharing(aBrowser, aState) {
      let tab = this.getTabForBrowser(aBrowser);
      if (!tab) {
        return;
      }
      if (tab._sharingState == null) {
        tab._sharingState = {};
      }
      tab._sharingState = Object.assign(tab._sharingState, aState);

      if ("webRTC" in aState) {
        if (tab._sharingState.webRTC?.sharing) {
          if (tab._sharingState.webRTC.paused) {
            tab.removeAttribute("sharing");
          } else {
            tab.setAttribute("sharing", aState.webRTC.sharing);
          }
        } else {
          tab.removeAttribute("sharing");
        }
        this._tabAttrModified(tab, ["sharing"]);
      }

      if (aBrowser == this.selectedBrowser) {
        gPermissionPanel.updateSharingIndicator();
      }
    }

    getTabSharingState(aTab) {
      // Normalize the state object for consumers (ie.extensions).
      let state = Object.assign(
        {},
        aTab._sharingState && aTab._sharingState.webRTC
      );
      return {
        camera: !!state.camera,
        microphone: !!state.microphone,
        screen: state.screen && state.screen.replace("Paused"""),
      };
    }

    setInitialTabTitle(aTab, aTitle, aOptions = {}) {
      // Convert some non-content title (actually a url) to human readable title
      if (!aOptions.isContentTitle && isBlankPageURL(aTitle)) {
        aTitle = this.tabContainer.emptyTabTitle;
      }

      if (aTitle) {
        if (!aTab.getAttribute("label")) {
          aTab._labelIsInitialTitle = true;
        }

        this._setTabLabel(aTab, aTitle, aOptions);
      }
    }

    _dataURLRegEx = /^data:[^,]+;base64,/i;

    // Regex to test if a string (potential tab label) consists of only non-
    // printable characters. We consider Unicode categories Separator
    // (spaces & line-breaks) and Other (control chars, private use, non-
    // character codepoints) to be unprintable, along with a few specific
    // characters whose expected rendering is blank:
    //   U+2800 BRAILLE PATTERN BLANK (category So)
    //   U+115F HANGUL CHOSEONG FILLER (category Lo)
    //   U+1160 HANGUL JUNGSEONG FILLER (category Lo)
    //   U+3164 HANGUL FILLER (category Lo)
    //   U+FFA0 HALFWIDTH HANGUL FILLER (category Lo)
    // We also ignore combining marks, as in the absence of a printable base
    // character they are unlikely to be usefully rendered, and may well be
    // clipped away entirely.
    _nonPrintingRegEx =
      /^[\p{Z}\p{C}\p{M}\u{115f}\u{1160}\u{2800}\u{3164}\u{ffa0}]*$/u;

    setTabTitle(aTab) {
      var browser = this.getBrowserForTab(aTab);
      var title = browser.contentTitle;

      if (aTab.hasAttribute("customizemode")) {
        title = this.tabLocalization.formatValueSync(
          "tabbrowser-customizemode-tab-title"
        );
      }

      // Don't replace an initially set label with the URL while the tab
      // is loading.
      if (aTab._labelIsInitialTitle) {
        if (!title) {
          return false;
        }
        delete aTab._labelIsInitialTitle;
      }

      let isURL = false;

      // Trim leading and trailing whitespace from the title.
      title = title.trim();

      // If the title contains only non-printing characters (or only combining
      // marks, but no base character for them), we won't use it.
      if (this._nonPrintingRegEx.test(title)) {
        title = "";
      }

      let isContentTitle = !!title;
      if (!title) {
        // See if we can use the URI as the title.
        if (browser.currentURI.displaySpec) {
          try {
            title = Services.io.createExposableURI(
              browser.currentURI
            ).displaySpec;
          } catch (ex) {
            title = browser.currentURI.displaySpec;
          }
        }

        if (title && !isBlankPageURL(title)) {
          isURL = true;
          if (title.length <= 500 || !this._dataURLRegEx.test(title)) {
            // Try to unescape not-ASCII URIs using the current character set.
            try {
              let characterSet = browser.characterSet;
              title = Services.textToSubURI.unEscapeNonAsciiURI(
                characterSet,
                title
              );
            } catch (ex) {
              /* Do nothing. */
            }
          }
        } else {
          // No suitable URI? Fall back to our untitled string.
          title = this.tabContainer.emptyTabTitle;
        }
      }

      return this._setTabLabel(aTab, title, { isContentTitle, isURL });
    }

    // While an auth prompt from a base domain different than the current sites is open, we do not want to show the tab title of the current site,
    // but of the origin that is requesting authentication.
    // This is to prevent possible auth spoofing scenarios.
    // See bug 791594 for reference.
    setTabLabelForAuthPrompts(aTab, aLabel) {
      return this._setTabLabel(aTab, aLabel);
    }

    _setTabLabel(aTab, aLabel, { beforeTabOpen, isContentTitle, isURL } = {}) {
      if (!aLabel || aLabel.includes("about:reader?")) {
        return false;
      }

      // If it's a long data: URI that uses base64 encoding, truncate to a
      // reasonable length rather than trying to display the entire thing,
      // which can hang or crash the browser.
      // We can't shorten arbitrary URIs like this, as bidi etc might mean
      // we need the trailing characters for display. But a base64-encoded
      // data-URI is plain ASCII, so this is OK for tab-title display.
      // (See bug 1408854.)
      if (isURL && aLabel.length > 500 && this._dataURLRegEx.test(aLabel)) {
        aLabel = aLabel.substring(0, 500) + "\u2026";
      }

      aTab._fullLabel = aLabel;

      if (!isContentTitle) {
        // Remove protocol and "www."
        if (!("_regex_shortenURLForTabLabel" in this)) {
          this._regex_shortenURLForTabLabel = /^[^:]+:\/\/(?:www\.)?/;
        }
        aLabel = aLabel.replace(this._regex_shortenURLForTabLabel, "");
      }

      aTab._labelIsContentTitle = isContentTitle;

      if (aTab.getAttribute("label") == aLabel) {
        return false;
      }

      let dwu = window.windowUtils;
      let isRTL =
        dwu.getDirectionFromText(aLabel) == Ci.nsIDOMWindowUtils.DIRECTION_RTL;

      aTab.setAttribute("label", aLabel);
      aTab.setAttribute("labeldirection", isRTL ? "rtl" : "ltr");
      aTab.toggleAttribute("labelendaligned", isRTL != (document.dir == "rtl"));

      // Dispatch TabAttrModified event unless we're setting the label
      // before the TabOpen event was dispatched.
      if (!beforeTabOpen) {
        this._tabAttrModified(aTab, ["label"]);
      }

      if (aTab.selected) {
        this.updateTitlebar();
      }

      return true;
    }

    loadTabs(
      aURIs,
      {
        allowInheritPrincipal,
        allowThirdPartyFixup,
        inBackground,
        newIndex,
        postDatas,
        replace,
        targetTab,
        triggeringPrincipal,
        csp,
        userContextId,
        fromExternal,
      } = {}
    ) {
      if (!aURIs.length) {
        return;
      }

      // The tab selected after this new tab is closed (i.e. the new tab's
      // "owner") is the next adjacent tab (i.e. not the previously viewed tab)
      // when several urls are opened here (i.e. closing the first should select
      // the next of many URLs opened) or if the pref to have UI links opened in
      // the background is set (i.e. the link is not being opened modally)
      //
      // i.e.
      //    Number of URLs    Load UI Links in BG       Focus Last Viewed?
      //    == 1              false                     YES
      //    == 1              true                      NO
      //    > 1               false/true                NO
      var multiple = aURIs.length > 1;
      var owner = multiple || inBackground ? null : this.selectedTab;
      var firstTabAdded = null;
      var targetTabIndex = -1;

      if (typeof newIndex != "number") {
        newIndex = -1;
      }

      // When bulk opening tabs, such as from a bookmark folder, we want to insertAfterCurrent
      // if necessary, but we also will set the bulkOrderedOpen flag so that the bookmarks
      // open in the same order they are in the folder.
      if (
        multiple &&
        newIndex < 0 &&
        Services.prefs.getBoolPref("browser.tabs.insertAfterCurrent")
      ) {
        newIndex = this.selectedTab._tPos + 1;
      }

      if (replace) {
        let browser;
        if (targetTab) {
          browser = this.getBrowserForTab(targetTab);
          targetTabIndex = targetTab._tPos;
        } else {
          browser = this.selectedBrowser;
          targetTabIndex = this.tabContainer.selectedIndex;
        }
        let loadFlags = LOAD_FLAGS_NONE;
        if (allowThirdPartyFixup) {
          loadFlags |=
            LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
        }
        if (!allowInheritPrincipal) {
          loadFlags |= LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
        }
        if (fromExternal) {
          loadFlags |= LOAD_FLAGS_FROM_EXTERNAL;
        }
        try {
          browser.fixupAndLoadURIString(aURIs[0], {
            loadFlags,
            postData: postDatas && postDatas[0],
            triggeringPrincipal,
            csp,
          });
        } catch (e) {
          // Ignore failure in case a URI is wrong, so we can continue
          // opening the next ones.
        }
      } else {
        let params = {
          allowInheritPrincipal,
          ownerTab: owner,
          skipAnimation: multiple,
          allowThirdPartyFixup,
          postData: postDatas && postDatas[0],
          userContextId,
          triggeringPrincipal,
          bulkOrderedOpen: multiple,
          csp,
          fromExternal,
        };
        if (newIndex > -1) {
          params.index = newIndex;
        }
        firstTabAdded = this.addTab(aURIs[0], params);
        if (newIndex > -1) {
          targetTabIndex = firstTabAdded._tPos;
        }
      }

      let tabNum = targetTabIndex;
      for (let i = 1; i < aURIs.length; ++i) {
        let params = {
          allowInheritPrincipal,
          skipAnimation: true,
          allowThirdPartyFixup,
          postData: postDatas && postDatas[i],
          userContextId,
          triggeringPrincipal,
          bulkOrderedOpen: true,
          csp,
          fromExternal,
        };
        if (targetTabIndex > -1) {
          params.index = ++tabNum;
        }
        this.addTab(aURIs[i], params);
      }

      if (firstTabAdded && !inBackground) {
        this.selectedTab = firstTabAdded;
      }
    }

    updateBrowserRemoteness(aBrowser, { newFrameloader, remoteType } = {}) {
      let isRemote = aBrowser.getAttribute("remote") == "true";

      // We have to be careful with this here, as the "no remote type" is null,
      // not a string. Make sure to check only for undefined, since null is
      // allowed.
      if (remoteType === undefined) {
        throw new Error("Remote type must be set!");
      }

      let shouldBeRemote = remoteType !== E10SUtils.NOT_REMOTE;

      if (!gMultiProcessBrowser && shouldBeRemote) {
        throw new Error(
          "Cannot switch to remote browser in a window " +
            "without the remote tabs load context."
        );
      }

      // Abort if we're not going to change anything
      let oldRemoteType = aBrowser.remoteType;
      if (
        isRemote == shouldBeRemote &&
        !newFrameloader &&
        (!isRemote || oldRemoteType == remoteType)
      ) {
        return false;
      }

      let tab = this.getTabForBrowser(aBrowser);
      // aBrowser needs to be inserted now if it hasn't been already.
      this._insertBrowser(tab);

      let evt = document.createEvent("Events");
      evt.initEvent("BeforeTabRemotenessChange"truefalse);
      tab.dispatchEvent(evt);

      // Unhook our progress listener.
      let filter = this._tabFilters.get(tab);
      let listener = this._tabListeners.get(tab);
      aBrowser.webProgress.removeProgressListener(filter);
      filter.removeProgressListener(listener);

      // We'll be creating a new listener, so destroy the old one.
      listener.destroy();

      let oldDroppedLinkHandler = aBrowser.droppedLinkHandler;
      let oldUserTypedValue = aBrowser.userTypedValue;
      let hadStartedLoad = aBrowser.didStartLoadSinceLastUserTyping();

      // Change the "remote" attribute.

      // Make sure the browser is destroyed so it unregisters from observer notifications
      aBrowser.destroy();

      if (shouldBeRemote) {
        aBrowser.setAttribute("remote""true");
        aBrowser.setAttribute("remoteType", remoteType);
      } else {
        aBrowser.setAttribute("remote""false");
        aBrowser.removeAttribute("remoteType");
      }

      // This call actually switches out our frameloaders. Do this as late as
      // possible before rebuilding the browser, as we'll need the new browser
      // state set up completely first.
      aBrowser.changeRemoteness({
        remoteType,
      });

      // Once we have new frameloaders, this call sets the browser back up.
      aBrowser.construct();

      aBrowser.userTypedValue = oldUserTypedValue;
      if (hadStartedLoad) {
        aBrowser.urlbarChangeTracker.startedLoad();
      }

      aBrowser.droppedLinkHandler = oldDroppedLinkHandler;

      // This shouldn't really be necessary, however, this has the side effect
      // of sending MozLayerTreeReady / MozLayerTreeCleared events for remote
      // frames, which the tab switcher depends on.
      //
      // eslint-disable-next-line no-self-assign
      aBrowser.docShellIsActive = aBrowser.docShellIsActive;

      // Create a new tab progress listener for the new browser we just injected,
      // since tab progress listeners have logic for handling the initial about:blank
      // load
      listener = new TabProgressListener(tab, aBrowser, truefalse);
      this._tabListeners.set(tab, listener);
      filter.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_ALL);

      // Restore the progress listener.
      aBrowser.webProgress.addProgressListener(
        filter,
        Ci.nsIWebProgress.NOTIFY_ALL
      );

      // Restore the securityUI state.
      let securityUI = aBrowser.securityUI;
      let state = securityUI
        ? securityUI.state
        : Ci.nsIWebProgressListener.STATE_IS_INSECURE;
      this._callProgressListeners(
        aBrowser,
        "onSecurityChange",
        [aBrowser.webProgress, null, state],
        true,
        false
      );
      let event = aBrowser.getContentBlockingEvents();
      // Include the true final argument to indicate that this event is
      // simulated (instead of being observed by the webProgressListener).
      this._callProgressListeners(
        aBrowser,
        "onContentBlockingEvent",
        [aBrowser.webProgress, null, event, true],
        true,
        false
      );

      if (shouldBeRemote) {
        // Switching the browser to be remote will connect to a new child
        // process so the browser can no longer be considered to be
        // crashed.
        tab.removeAttribute("crashed");
      }

      // If the findbar has been initialised, reset its browser reference.
      if (this.isFindBarInitialized(tab)) {
        this.getCachedFindBar(tab).browser = aBrowser;
      }

      evt = document.createEvent("Events");
      evt.initEvent("TabRemotenessChange"truefalse);
      tab.dispatchEvent(evt);

      return true;
    }

    updateBrowserRemotenessByURL(aBrowser, aURL, aOptions = {}) {
      if (!gMultiProcessBrowser) {
        return this.updateBrowserRemoteness(aBrowser, {
          remoteType: E10SUtils.NOT_REMOTE,
        });
      }

      let oldRemoteType = aBrowser.remoteType;

      let oa = E10SUtils.predictOriginAttributes({ browser: aBrowser });

      aOptions.remoteType = E10SUtils.getRemoteTypeForURI(
        aURL,
        gMultiProcessBrowser,
        gFissionBrowser,
        oldRemoteType,
        aBrowser.currentURI,
        oa
      );

      // If this URL can't load in the current browser then flip it to the
      // correct type.
      if (oldRemoteType != aOptions.remoteType || aOptions.newFrameloader) {
        return this.updateBrowserRemoteness(aBrowser, aOptions);
      }

      return false;
    }

    createBrowser({
      isPreloadBrowser,
      name,
      openWindowInfo,
      remoteType,
      initialBrowsingContextGroupId,
      uriIsAboutBlank,
      userContextId,
      skipLoad,
    } = {}) {
      let b = document.createXULElement("browser");
      // Use the JSM global to create the permanentKey, so that if the
      // permanentKey is held by something after this window closes, it
      // doesn't keep the window alive.
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=99 G=94

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.