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

Quelle  main.js   Sprache: JAVA

 
/* 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/. */


/* import-globals-from extensionControlled.js */
/* import-globals-from preferences.js */
/* import-globals-from /toolkit/mozapps/preferences/fontbuilder.js */
/* import-globals-from /browser/base/content/aboutDialog-appUpdater.js */
/* global MozXULElement */

ChromeUtils.defineESModuleGetters(this, {
  BackgroundUpdate: "resource://gre/modules/BackgroundUpdate.sys.mjs",
  UpdateListener: "resource://gre/modules/UpdateListener.sys.mjs",
  MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs",
  TranslationsParent: "resource://gre/actors/TranslationsParent.sys.mjs",
  WindowsLaunchOnLogin: "resource://gre/modules/WindowsLaunchOnLogin.sys.mjs",
  NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
});

// Constants & Enumeration Values
const TYPE_PDF = "application/pdf";

const PREF_PDFJS_DISABLED = "pdfjs.disabled";

// Pref for when containers is being controlled
const PREF_CONTAINERS_EXTENSION = "privacy.userContext.extension";

// Strings to identify ExtensionSettingsStore overrides
const CONTAINERS_KEY = "privacy.containers";

const PREF_USE_SYSTEM_COLORS = "browser.display.use_system_colors";
const PREF_CONTENT_APPEARANCE =
  "layout.css.prefers-color-scheme.content-override";
const FORCED_COLORS_QUERY = matchMedia("(forced-colors)");

const AUTO_UPDATE_CHANGED_TOPIC =
  UpdateUtils.PER_INSTALLATION_PREFS["app.update.auto"].observerTopic;
const BACKGROUND_UPDATE_CHANGED_TOPIC =
  UpdateUtils.PER_INSTALLATION_PREFS["app.update.background.enabled"]
    .observerTopic;

const ICON_URL_APP =
  AppConstants.platform == "linux"
    ? "moz-icon://dummy.exe?size=16"
    : "chrome://browser/skin/preferences/application.png";

// For CSS. Can be one of "ask", "save" or "handleInternally". If absent, the icon URL
// was set by us to a custom handler icon and CSS should not try to override it.
const APP_ICON_ATTR_NAME = "appHandlerIcon";

Preferences.addAll([
  // Startup
  { id: "browser.startup.page", type: "int" },
  { id: "browser.privatebrowsing.autostart", type: "bool" },

  // Downloads
  { id: "browser.download.useDownloadDir", type: "bool", inverted: true },
  { id: "browser.download.always_ask_before_handling_new_types", type: "bool" },
  { id: "browser.download.folderList", type: "int" },
  { id: "browser.download.dir", type: "file" },

  /* Tab preferences
  Preferences:

  browser.link.open_newwindow
      1 opens such links in the most recent window or tab,
      2 opens such links in a new window,
      3 opens such links in a new tab
  browser.tabs.loadInBackground
  - true if display should switch to a new tab which has been opened from a
    link, false if display shouldn't switch
  browser.tabs.warnOnClose
  - true if when closing a window with multiple tabs the user is warned and
    allowed to cancel the action, false to just close the window
  browser.tabs.warnOnOpen
  - true if the user should be warned if he attempts to open a lot of tabs at
    once (e.g. a large folder of bookmarks), false otherwise
  browser.warnOnQuitShortcut
  - true if the user should be warned if they quit using the keyboard shortcut
  browser.taskbar.previews.enable
  - true if tabs are to be shown in the Windows 7 taskbar
  */


  { id: "browser.link.open_newwindow", type: "int" },
  { id: "browser.tabs.loadInBackground", type: "bool", inverted: true },
  { id: "browser.tabs.warnOnClose", type: "bool" },
  { id: "browser.warnOnQuitShortcut", type: "bool" },
  { id: "browser.tabs.warnOnOpen", type: "bool" },
  { id: "browser.ctrlTab.sortByRecentlyUsed", type: "bool" },
  { id: "browser.tabs.hoverPreview.enabled", type: "bool" },
  { id: "browser.tabs.hoverPreview.showThumbnails", type: "bool" },

  { id: "sidebar.verticalTabs", type: "bool" },
  { id: "sidebar.revamp", type: "bool" },

  // CFR
  {
    id: "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
    type: "bool",
  },
  {
    id: "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
    type: "bool",
  },

  // Fonts
  { id: "font.language.group", type: "wstring" },

  // Languages
  { id: "intl.regional_prefs.use_os_locales", type: "bool" },

  // General tab

  /* Accessibility
   * accessibility.browsewithcaret
     - true enables keyboard navigation and selection within web pages using a
       visible caret, false uses normal keyboard navigation with no caret
   * accessibility.typeaheadfind
     - when set to true, typing outside text areas and input boxes will
       automatically start searching for what's typed within the current
       document; when set to false, no search action happens */

  { id: "accessibility.browsewithcaret", type: "bool" },
  { id: "accessibility.typeaheadfind", type: "bool" },
  { id: "accessibility.blockautorefresh", type: "bool" },

  /* Browsing
   * general.autoScroll
     - when set to true, clicking the scroll wheel on the mouse activates a
       mouse mode where moving the mouse down scrolls the document downward with
       speed correlated with the distance of the cursor from the original
       position at which the click occurred (and likewise with movement upward);
       if false, this behavior is disabled
   * general.smoothScroll
     - set to true to enable finer page scrolling than line-by-line on page-up,
       page-down, and other such page movements */

  { id: "general.autoScroll", type: "bool" },
  { id: "general.smoothScroll", type: "bool" },
  { id: "widget.gtk.overlay-scrollbars.enabled", type: "bool", inverted: true },
  { id: "layout.css.always_underline_links", type: "bool" },
  { id: "layout.spellcheckDefault", type: "int" },
  { id: "accessibility.tabfocus", type: "int" },

  {
    id: "browser.preferences.defaultPerformanceSettings.enabled",
    type: "bool",
  },
  { id: "dom.ipc.processCount", type: "int" },
  { id: "dom.ipc.processCount.web", type: "int" },
  { id: "layers.acceleration.disabled", type: "bool", inverted: true },

  // Files and Applications
  { id: "pref.downloads.disable_button.edit_actions", type: "bool" },

  // DRM content
  { id: "media.eme.enabled", type: "bool" },

  // Update
  { id: "browser.preferences.advanced.selectedTabIndex", type: "int" },
  { id: "browser.search.update", type: "bool" },

  { id: "privacy.userContext.enabled", type: "bool" },
  {
    id: "privacy.userContext.newTabContainerOnLeftClick.enabled",
    type: "bool",
  },

  // Picture-in-Picture
  {
    id: "media.videocontrols.picture-in-picture.video-toggle.enabled",
    type: "bool",
  },

  // Media
  { id: "media.hardwaremediakeys.enabled", type: "bool" },
]);

if (AppConstants.HAVE_SHELL_SERVICE) {
  Preferences.addAll([
    { id: "browser.shell.checkDefaultBrowser", type: "bool" },
    { id: "pref.general.disable_button.default_browser", type: "bool" },
  ]);
}

if (AppConstants.platform === "win") {
  Preferences.addAll([
    { id: "browser.taskbar.previews.enable", type: "bool" },
    { id: "ui.osk.enabled", type: "bool" },
  ]);
}

if (AppConstants.MOZ_UPDATER) {
  Preferences.addAll([
    { id: "app.update.disable_button.showUpdateHistory", type: "bool" },
  ]);

  if (AppConstants.NIGHTLY_BUILD) {
    Preferences.addAll([{ id: "app.update.suppressPrompts", type: "bool" }]);
  }
}

ChromeUtils.defineLazyGetter(this"gIsPackagedApp", () => {
  return Services.sysinfo.getProperty("isPackagedApp");
});

// A promise that resolves when the list of application handlers is loaded.
// We store this in a global so tests can await it.
var promiseLoadHandlersList;

// Load the preferences string bundle for other locales with fallbacks.
function getBundleForLocales(newLocales) {
  let locales = Array.from(
    new Set([
      ...newLocales,
      ...Services.locale.requestedLocales,
      Services.locale.lastFallbackLocale,
    ])
  );
  return new Localization(
    ["browser/preferences/preferences.ftl""branding/brand.ftl"],
    false,
    undefined,
    locales
  );
}

var gNodeToObjectMap = new WeakMap();

var gMainPane = {
  // The set of types the app knows how to handle.  A hash of HandlerInfoWrapper
  // objects, indexed by type.
  _handledTypes: {},

  // The list of types we can show, sorted by the sort column/direction.
  // An array of HandlerInfoWrapper objects.  We build this list when we first
  // load the data and then rebuild it when users change a pref that affects
  // what types we can show or change the sort column/direction.
  // Note: this isn't necessarily the list of types we *will* show; if the user
  // provides a filter string, we'll only show the subset of types in this list
  // that match that string.
  _visibleTypes: [],

  // browser.startup.page values
  STARTUP_PREF_BLANK: 0,
  STARTUP_PREF_HOMEPAGE: 1,
  STARTUP_PREF_RESTORE_SESSION: 3,

  // Convenience & Performance Shortcuts

  get _list() {
    delete this._list;
    return (this._list = document.getElementById("handlersView"));
  },

  get _filter() {
    delete this._filter;
    return (this._filter = document.getElementById("filter"));
  },

  _backoffIndex: 0,

  /**
   * Initialization of gMainPane.
   */

  init() {
    function setEventListener(aId, aEventType, aCallback) {
      document
        .getElementById(aId)
        .addEventListener(aEventType, aCallback.bind(gMainPane));
    }

    if (AppConstants.HAVE_SHELL_SERVICE) {
      this.updateSetDefaultBrowser();
      let win = Services.wm.getMostRecentWindow("navigator:browser");

      // Exponential backoff mechanism will delay the polling times if user doesn't
      // trigger SetDefaultBrowser for a long time.
      let backoffTimes = [
        1000, 1000, 1000, 1000, 2000, 2000, 2000, 5000, 5000, 10000,
      ];

      let pollForDefaultBrowser = () => {
        let uri = win.gBrowser.currentURI.spec;

        if (
          (uri == "about:preferences" ||
            uri == "about:preferences#general" ||
            uri == "about:settings" ||
            uri == "about:settings#general") &&
          document.visibilityState == "visible"
        ) {
          this.updateSetDefaultBrowser();
        }

        // approximately a "requestIdleInterval"
        window.setTimeout(
          () => {
            window.requestIdleCallback(pollForDefaultBrowser);
          },
          backoffTimes[
            this._backoffIndex + 1 < backoffTimes.length
              ? this._backoffIndex++
              : backoffTimes.length - 1
          ]
        );
      };

      window.setTimeout(() => {
        window.requestIdleCallback(pollForDefaultBrowser);
      }, backoffTimes[this._backoffIndex]);
    }

    this.initBrowserContainers();
    this.buildContentProcessCountMenuList();

    this.updateDefaultPerformanceSettingsPref();

    let defaultPerformancePref = Preferences.get(
      "browser.preferences.defaultPerformanceSettings.enabled"
    );
    defaultPerformancePref.on("change", () => {
      this.updatePerformanceSettingsBox({ duringChangeEvent: true });
    });
    this.updatePerformanceSettingsBox({ duringChangeEvent: false });
    this.displayUseSystemLocale();
    this.updateProxySettingsUI();
    initializeProxyUI(gMainPane);

    if (Services.prefs.getBoolPref("intl.multilingual.enabled")) {
      gMainPane.initPrimaryBrowserLanguageUI();
    }

    // We call `initDefaultZoomValues` to set and unhide the
    // default zoom preferences menu, and to establish a
    // listener for future menu changes.
    gMainPane.initDefaultZoomValues();

    gMainPane.initTranslations();

    if (
      Services.prefs.getBoolPref(
        "media.videocontrols.picture-in-picture.enabled"
      )
    ) {
      document.getElementById("pictureInPictureBox").hidden = false;
      setEventListener(
        "pictureInPictureToggleEnabled",
        "command",
        function (event) {
          if (!event.target.checked) {
            Glean.pictureinpictureSettings.disableSettings.record();
          }
        }
      );
    }

    if (AppConstants.platform == "win") {
      // Functionality for "Show tabs in taskbar" on Windows 7 and up.
      try {
        let ver = parseFloat(Services.sysinfo.getProperty("version"));
        let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
        showTabsInTaskbar.hidden = ver < 6.1;
      } catch (ex) {}
    }

    let thumbsCheckbox = document.getElementById("tabPreviewShowThumbnails");
    let cardPreviewEnabledPref = Preferences.get(
      "browser.tabs.hoverPreview.enabled"
    );
    let maybeShowThumbsCheckbox = () =>
      (thumbsCheckbox.hidden = !cardPreviewEnabledPref.value);
    cardPreviewEnabledPref.on("change", maybeShowThumbsCheckbox);
    maybeShowThumbsCheckbox();

    // The "opening multiple tabs might slow down Firefox" warning provides
    // an option for not showing this warning again. When the user disables it,
    // we provide checkboxes to re-enable the warning.
    if (!TransientPrefs.prefShouldBeVisible("browser.tabs.warnOnOpen")) {
      document.getElementById("warnOpenMany").hidden = true;
    }

    if (AppConstants.platform != "win") {
      let quitKeyElement =
        window.browsingContext.topChromeWindow.document.getElementById(
          "key_quitApplication"
        );
      if (quitKeyElement) {
        let quitKey = ShortcutUtils.prettifyShortcut(quitKeyElement);
        document.l10n.setAttributes(
          document.getElementById("warnOnQuitKey"),
          "ask-on-quit-with-key",
          { quitKey }
        );
      } else {
        // If the quit key element does not exist, then the quit key has
        // been disabled, so just hide the checkbox.
        document.getElementById("warnOnQuitKey").hidden = true;
      }
    }

    setEventListener("ctrlTabRecentlyUsedOrder""command"function () {
      Services.prefs.clearUserPref("browser.ctrlTab.migrated");
    });
    setEventListener("manageBrowserLanguagesButton""command"function () {
      gMainPane.showBrowserLanguagesSubDialog({ search: false });
    });
    if (AppConstants.MOZ_UPDATER) {
      // These elements are only compiled in when the updater is enabled
      setEventListener("checkForUpdatesButton""command"function () {
        gAppUpdater.checkForUpdates();
      });
      setEventListener("downloadAndInstallButton""command"function () {
        gAppUpdater.startDownload();
      });
      setEventListener("updateButton""command"function () {
        gAppUpdater.buttonRestartAfterDownload();
      });
      setEventListener("checkForUpdatesButton2""command"function () {
        gAppUpdater.checkForUpdates();
      });
      setEventListener("checkForUpdatesButton3""command"function () {
        gAppUpdater.checkForUpdates();
      });
      setEventListener("checkForUpdatesButton4""command"function () {
        gAppUpdater.checkForUpdates();
      });
    }

    // Startup pref
    setEventListener(
      "browserRestoreSession",
      "command",
      gMainPane.onBrowserRestoreSessionChange
    );
    if (AppConstants.platform == "win") {
      setEventListener(
        "windowsLaunchOnLogin",
        "command",
        gMainPane.onWindowsLaunchOnLoginChange
      );
      if (
        Services.prefs.getBoolPref(
          "browser.startup.windowsLaunchOnLogin.enabled",
          false
        )
      ) {
        document.getElementById("windowsLaunchOnLoginBox").hidden = false;
        NimbusFeatures.windowsLaunchOnLogin.recordExposureEvent({
          once: true,
        });
      }
    }
    gMainPane.updateBrowserStartupUI =
      gMainPane.updateBrowserStartupUI.bind(gMainPane);
    Preferences.get("browser.privatebrowsing.autostart").on(
      "change",
      gMainPane.updateBrowserStartupUI
    );
    Preferences.get("browser.startup.page").on(
      "change",
      gMainPane.updateBrowserStartupUI
    );
    Preferences.get("browser.startup.homepage").on(
      "change",
      gMainPane.updateBrowserStartupUI
    );
    gMainPane.updateBrowserStartupUI();

    if (AppConstants.HAVE_SHELL_SERVICE) {
      setEventListener(
        "setDefaultButton",
        "command",
        gMainPane.setDefaultBrowser
      );
    }
    setEventListener(
      "disableContainersExtension",
      "command",
      makeDisableControllingExtension(PREF_SETTING_TYPE, CONTAINERS_KEY)
    );
    setEventListener("chooseLanguage""command", gMainPane.showLanguages);
    // TODO (Bug 1817084) Remove this code when we disable the extension
    setEventListener(
      "fxtranslateButton",
      "command",
      gMainPane.showTranslationExceptions
    );
    Preferences.get("font.language.group").on(
      "change",
      gMainPane._rebuildFonts.bind(gMainPane)
    );
    setEventListener("advancedFonts""command", gMainPane.configureFonts);
    setEventListener("colors""command", gMainPane.configureColors);
    Preferences.get("layers.acceleration.disabled").on(
      "change",
      gMainPane.updateHardwareAcceleration.bind(gMainPane)
    );
    setEventListener(
      "connectionSettings",
      "command",
      gMainPane.showConnections
    );
    setEventListener(
      "browserContainersCheckbox",
      "command",
      gMainPane.checkBrowserContainers
    );
    setEventListener(
      "browserContainersSettings",
      "command",
      gMainPane.showContainerSettings
    );
    setEventListener(
      "data-migration",
      "command",
      gMainPane.onMigrationButtonCommand
    );

    document
      .getElementById("migrationWizardDialog")
      .addEventListener("MigrationWizard:Close"function (e) {
        e.currentTarget.close();
      });

    if (Services.policies && !Services.policies.isAllowed("profileImport")) {
      document.getElementById("dataMigrationGroup").remove();
    }

    if (
      Services.prefs.getBoolPref("browser.backup.preferences.ui.enabled"false)
    ) {
      let backupGroup = document.getElementById("dataBackupGroup");
      backupGroup.removeAttribute("data-hidden-from-search");
    }

    // For media control toggle button, we support it on Windows, macOS and
    // gtk-based Linux.
    if (
      AppConstants.platform == "win" ||
      AppConstants.platform == "macosx" ||
      AppConstants.MOZ_WIDGET_GTK
    ) {
      document.getElementById("mediaControlBox").hidden = false;
    }

    // Initializes the fonts dropdowns displayed in this pane.
    this._rebuildFonts();

    // Firefox Translations settings panel
    // TODO (Bug 1817084) Remove this code when we disable the extension
    const fxtranslationsDisabledPrefName = "extensions.translations.disabled";
    if (!Services.prefs.getBoolPref(fxtranslationsDisabledPrefName, true)) {
      let fxtranslationRow = document.getElementById("fxtranslationsBox");
      fxtranslationRow.hidden = false;
    }

    let emeUIEnabled = Services.prefs.getBoolPref("browser.eme.ui.enabled");
    // Force-disable/hide on WinXP:
    if (navigator.platform.toLowerCase().startsWith("win")) {
      emeUIEnabled =
        emeUIEnabled && parseFloat(Services.sysinfo.get("version")) >= 6;
    }
    if (!emeUIEnabled) {
      // Don't want to rely on .hidden for the toplevel groupbox because
      // of the pane hiding/showing code potentially interfering:
      document
        .getElementById("drmGroup")
        .setAttribute("style""display: none !important");
    }
    // Initialize the Firefox Updates section.
    let version = AppConstants.MOZ_APP_VERSION_DISPLAY;

    // Include the build ID if this is an "a#" (nightly) build
    if (/a\d+$/.test(version)) {
      let buildID = Services.appinfo.appBuildID;
      let year = buildID.slice(0, 4);
      let month = buildID.slice(4, 6);
      let day = buildID.slice(6, 8);
      version += ` (${year}-${month}-${day})`;
    }

    // Append "(32-bit)" or "(64-bit)" build architecture to the version number:
    let bundle = Services.strings.createBundle(
      "chrome://browser/locale/browser.properties"
    );
    let archResource = Services.appinfo.is64Bit
      ? "aboutDialog.architecture.sixtyFourBit"
      : "aboutDialog.architecture.thirtyTwoBit";
    let arch = bundle.GetStringFromName(archResource);
    version += ` (${arch})`;

    document.l10n.setAttributes(
      document.getElementById("updateAppInfo"),
      "update-application-version",
      { version }
    );

    // Show a release notes link if we have a URL.
    let relNotesLink = document.getElementById("releasenotes");
    let relNotesPrefType = Services.prefs.getPrefType("app.releaseNotesURL");
    if (relNotesPrefType != Services.prefs.PREF_INVALID) {
      let relNotesURL = Services.urlFormatter.formatURLPref(
        "app.releaseNotesURL"
      );
      if (relNotesURL != "about:blank") {
        relNotesLink.href = relNotesURL;
        relNotesLink.hidden = false;
      }
    }

    let defaults = Services.prefs.getDefaultBranch(null);
    let distroId = defaults.getCharPref("distribution.id""");
    if (distroId) {
      let distroString = distroId;

      let distroVersion = defaults.getCharPref("distribution.version""");
      if (distroVersion) {
        distroString += " - " + distroVersion;
      }

      let distroIdField = document.getElementById("distributionId");
      distroIdField.value = distroString;
      distroIdField.hidden = false;

      let distroAbout = defaults.getStringPref("distribution.about""");
      if (distroAbout) {
        let distroField = document.getElementById("distribution");
        distroField.value = distroAbout;
        distroField.hidden = false;
      }
    }

    if (AppConstants.MOZ_UPDATER) {
      gAppUpdater = new appUpdater();
      setEventListener("showUpdateHistory""command", gMainPane.showUpdates);

      let updateDisabled =
        Services.policies && !Services.policies.isAllowed("appUpdate");

      if (gIsPackagedApp) {
        // When we're running inside an app package, there's no point in
        // displaying any update content here, and it would get confusing if we
        // did, because our updater is not enabled.
        // We can't rely on the hidden attribute for the toplevel elements,
        // because of the pane hiding/showing code interfering.
        document
          .getElementById("updatesCategory")
          .setAttribute("style""display: none !important");
        document
          .getElementById("updateApp")
          .setAttribute("style""display: none !important");
      } else if (
        updateDisabled ||
        UpdateUtils.appUpdateAutoSettingIsLocked() ||
        gApplicationUpdateService.manualUpdateOnly
      ) {
        document.getElementById("updateAllowDescription").hidden = true;
        document.getElementById("updateSettingsContainer").hidden = true;
      } else {
        // Start with no option selected since we are still reading the value
        document.getElementById("autoDesktop").removeAttribute("selected");
        document.getElementById("manualDesktop").removeAttribute("selected");
        // Start reading the correct value from the disk
        this.readUpdateAutoPref();
        setEventListener("updateRadioGroup""command", event => {
          if (event.target.id == "backgroundUpdate") {
            this.writeBackgroundUpdatePref();
          } else {
            this.writeUpdateAutoPref();
          }
        });
        if (this.isBackgroundUpdateUIAvailable()) {
          document.getElementById("backgroundUpdate").hidden = false;
          // Start reading the background update pref's value from the disk.
          this.readBackgroundUpdatePref();
        }
      }

      if (AppConstants.platform == "win") {
        // Check for a launch on login registry key
        // This accounts for if a user manually changes it in the registry
        // Disabling in Task Manager works outside of just deleting the registry key
        // in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run
        // but it is not possible to change it back to enabled as the disabled value is just a random
        // hexadecimal number
        let launchOnLoginCheckbox = document.getElementById(
          "windowsLaunchOnLogin"
        );

        let startWithLastProfile = Cc[
          "@mozilla.org/toolkit/profile-service;1"
        ].getService(Ci.nsIToolkitProfileService).startWithLastProfile;

        // Grey out the launch on login checkbox if startWithLastProfile is false
        document.getElementById(
          "windowsLaunchOnLoginDisabledProfileBox"
        ).hidden = startWithLastProfile;
        launchOnLoginCheckbox.disabled = !startWithLastProfile;

        if (!startWithLastProfile) {
          launchOnLoginCheckbox.checked = false;
        } else {
          WindowsLaunchOnLogin.getLaunchOnLoginEnabled().then(enabled => {
            launchOnLoginCheckbox.checked = enabled;
          });

          WindowsLaunchOnLogin.getLaunchOnLoginApproved().then(
            approvedByWindows => {
              launchOnLoginCheckbox.disabled = !approvedByWindows;
              document.getElementById(
                "windowsLaunchOnLoginDisabledBox"
              ).hidden = approvedByWindows;
            }
          );
        }

        // On Windows, the Application Update setting is an installation-
        // specific preference, not a profile-specific one. Show a warning to
        // inform users of this.
        let updateContainer = document.getElementById(
          "updateSettingsContainer"
        );
        updateContainer.classList.add("updateSettingCrossUserWarningContainer");
        document.getElementById("updateSettingCrossUserWarningDesc").hidden =
          false;
      }
    }

    // Initilize Application section.

    // Observe preferences that influence what we display so we can rebuild
    // the view when they change.
    Services.obs.addObserver(this, AUTO_UPDATE_CHANGED_TOPIC);
    Services.obs.addObserver(this, BACKGROUND_UPDATE_CHANGED_TOPIC);

    setEventListener("filter""command", gMainPane.filter);
    setEventListener("typeColumn""click", gMainPane.sort);
    setEventListener("actionColumn""click", gMainPane.sort);
    setEventListener("chooseFolder""command", gMainPane.chooseFolder);
    Preferences.get("browser.download.folderList").on(
      "change",
      gMainPane.displayDownloadDirPref.bind(gMainPane)
    );
    Preferences.get("browser.download.dir").on(
      "change",
      gMainPane.displayDownloadDirPref.bind(gMainPane)
    );
    gMainPane.displayDownloadDirPref();

    // Listen for window unload so we can remove our preference observers.
    window.addEventListener("unload"this);

    // Figure out how we should be sorting the list.  We persist sort settings
    // across sessions, so we can't assume the default sort column/direction.
    // XXX should we be using the XUL sort service instead?
    if (document.getElementById("actionColumn").hasAttribute("sortDirection")) {
      this._sortColumn = document.getElementById("actionColumn");
      // The typeColumn element always has a sortDirection attribute,
      // either because it was persisted or because the default value
      // from the xul file was used.  If we are sorting on the other
      // column, we should remove it.
      document.getElementById("typeColumn").removeAttribute("sortDirection");
    } else {
      this._sortColumn = document.getElementById("typeColumn");
    }

    appendSearchKeywords(
      "browserContainersSettings",
      [
        "user-context-personal",
        "user-context-work",
        "user-context-banking",
        "user-context-shopping",
      ].map(ContextualIdentityService.formatContextLabel)
    );

    AppearanceChooser.init();

    // Notify observers that the UI is now ready
    Services.obs.notifyObservers(window, "main-pane-loaded");

    Preferences.addSyncFromPrefListener(
      document.getElementById("defaultFont"),
      element => FontBuilder.readFontSelection(element)
    );
    if (AppConstants.platform == "macosx") {
      // We only expose this control on macOS, so don't try
      // to add listeners if it doesn't exist.
      Preferences.addSyncFromPrefListener(
        document.getElementById("useFullKeyboardNavigation"),
        () => this.readUseFullKeyboardNavigation()
      );
      Preferences.addSyncToPrefListener(
        document.getElementById("useFullKeyboardNavigation"),
        () => this.writeUseFullKeyboardNavigation()
      );
    }
    Preferences.addSyncFromPrefListener(
      document.getElementById("checkSpelling"),
      () => this.readCheckSpelling()
    );
    Preferences.addSyncToPrefListener(
      document.getElementById("checkSpelling"),
      () => this.writeCheckSpelling()
    );
    Preferences.addSyncFromPrefListener(
      document.getElementById("alwaysAsk"),
      () => this.readUseDownloadDir()
    );
    Preferences.addSyncFromPrefListener(
      document.getElementById("linkTargeting"),
      () => this.readLinkTarget()
    );
    Preferences.addSyncToPrefListener(
      document.getElementById("linkTargeting"),
      () => this.writeLinkTarget()
    );
    Preferences.addSyncFromPrefListener(
      document.getElementById("browserContainersCheckbox"),
      () => this.readBrowserContainersCheckbox()
    );

    this.setInitialized();
  },

  preInit() {
    promiseLoadHandlersList = new Promise((resolve, reject) => {
      // Load the data and build the list of handlers for applications pane.
      // By doing this after pageshow, we ensure it doesn't delay painting
      // of the preferences page.
      window.addEventListener(
        "pageshow",
        async () => {
          await this.initialized;
          try {
            this._initListEventHandlers();
            this._loadData();
            await this._rebuildVisibleTypes();
            await this._rebuildView();
            await this._sortListView();
            resolve();
          } catch (ex) {
            reject(ex);
          }
        },
        { once: true }
      );
    });
  },

  handleSubcategory(subcategory) {
    if (Services.policies && !Services.policies.isAllowed("profileImport")) {
      return false;
    }
    if (subcategory == "migrate") {
      this.showMigrationWizardDialog();
      return true;
    }

    if (subcategory == "migrate-autoclose") {
      this.showMigrationWizardDialog({ closeTabWhenDone: true });
    }

    return false;
  },

  // CONTAINERS

  /*
   * preferences:
   *
   * privacy.userContext.enabled
   * - true if containers is enabled
   */


  /**
   * Enables/disables the Settings button used to configure containers
   */

  readBrowserContainersCheckbox() {
    const pref = Preferences.get("privacy.userContext.enabled");
    const settings = document.getElementById("browserContainersSettings");

    settings.disabled = !pref.value;
    const containersEnabled = Services.prefs.getBoolPref(
      "privacy.userContext.enabled"
    );
    const containersCheckbox = document.getElementById(
      "browserContainersCheckbox"
    );
    containersCheckbox.checked = containersEnabled;
    handleControllingExtension(PREF_SETTING_TYPE, CONTAINERS_KEY).then(
      isControlled => {
        containersCheckbox.disabled = isControlled;
      }
    );
  },

  /**
   * Show the Containers UI depending on the privacy.userContext.ui.enabled pref.
   */

  initBrowserContainers() {
    if (!Services.prefs.getBoolPref("privacy.userContext.ui.enabled")) {
      // The browserContainersGroup element has its own internal padding that
      // is visible even if the browserContainersbox is visible, so hide the whole
      // groupbox if the feature is disabled to prevent a gap in the preferences.
      document
        .getElementById("browserContainersbox")
        .setAttribute("data-hidden-from-search""true");
      return;
    }
    Services.prefs.addObserver(PREF_CONTAINERS_EXTENSION, this);

    document.getElementById("browserContainersbox").hidden = false;
    this.readBrowserContainersCheckbox();
  },

  async onGetStarted() {
    if (!AppConstants.MOZ_DEV_EDITION) {
      return;
    }
    const win = Services.wm.getMostRecentWindow("navigator:browser");
    if (!win) {
      return;
    }
    const user = await fxAccounts.getSignedInUser();
    if (user) {
      // We have a user, open Sync preferences in the same tab
      win.openTrustedLinkIn("about:preferences#sync""current");
      return;
    }
    if (!(await FxAccounts.canConnectAccount())) {
      return;
    }
    let url =
      await FxAccounts.config.promiseConnectAccountURI("dev-edition-setup");
    let accountsTab = win.gBrowser.addWebTab(url);
    win.gBrowser.selectedTab = accountsTab;
  },

  // HOME PAGE
  /*
   * Preferences:
   *
   * browser.startup.page
   * - what page(s) to show when the user starts the application, as an integer:
   *
   *     0: a blank page (DEPRECATED - this can be set via browser.startup.homepage)
   *     1: the home page (as set by the browser.startup.homepage pref)
   *     2: the last page the user visited (DEPRECATED)
   *     3: windows and tabs from the last session (a.k.a. session restore)
   *
   *   The deprecated option is not exposed in UI; however, if the user has it
   *   selected and doesn't change the UI for this preference, the deprecated
   *   option is preserved.
   */


  /**
   * Utility function to enable/disable the button specified by aButtonID based
   * on the value of the Boolean preference specified by aPreferenceID.
   */

  updateButtons(aButtonID, aPreferenceID) {
    var button = document.getElementById(aButtonID);
    var preference = Preferences.get(aPreferenceID);
    button.disabled = !preference.value;
    return undefined;
  },

  /**
   * Hide/show the "Show my windows and tabs from last time" option based
   * on the value of the browser.privatebrowsing.autostart pref.
   */

  updateBrowserStartupUI() {
    const pbAutoStartPref = Preferences.get(
      "browser.privatebrowsing.autostart"
    );
    const startupPref = Preferences.get("browser.startup.page");

    let newValue;
    let checkbox = document.getElementById("browserRestoreSession");
    checkbox.disabled = pbAutoStartPref.value || startupPref.locked;
    newValue = pbAutoStartPref.value
      ? false
      : startupPref.value === this.STARTUP_PREF_RESTORE_SESSION;
    if (checkbox.checked !== newValue) {
      checkbox.checked = newValue;
    }
  },
  /**
   * Fetch the existing default zoom value, initialise and unhide
   * the preferences menu. This method also establishes a listener
   * to ensure handleDefaultZoomChange is called on future menu
   * changes.
   */

  async initDefaultZoomValues() {
    let win = window.browsingContext.topChromeWindow;
    let selected = await win.ZoomUI.getGlobalValue();
    let menulist = document.getElementById("defaultZoom");

    new SelectionChangedMenulist(menulist, event => {
      let parsedZoom = parseFloat((event.target.value / 100).toFixed(2));
      gMainPane.handleDefaultZoomChange(parsedZoom);
    });

    setEventListener("zoomText""command"function () {
      win.ZoomManager.toggleZoom();
      document.getElementById("text-zoom-override-warning").hidden =
        !document.getElementById("zoomText").checked;
    });

    let zoomValues = win.ZoomManager.zoomValues.map(a => {
      return Math.round(a * 100);
    });

    let fragment = document.createDocumentFragment();
    for (let zoomLevel of zoomValues) {
      let menuitem = document.createXULElement("menuitem");
      document.l10n.setAttributes(menuitem, "preferences-default-zoom-value", {
        percentage: zoomLevel,
      });
      menuitem.setAttribute("value", zoomLevel);
      fragment.appendChild(menuitem);
    }

    let menupopup = menulist.querySelector("menupopup");
    menupopup.appendChild(fragment);
    menulist.value = Math.round(selected * 100);

    let checkbox = document.getElementById("zoomText");
    checkbox.checked = !win.ZoomManager.useFullZoom;
    document.getElementById("text-zoom-override-warning").hidden =
      !checkbox.checked;
    document.getElementById("zoomBox").hidden = false;
  },

  /**
   * Initialize the translations view.
   */

  async initTranslations() {
    if (!Services.prefs.getBoolPref("browser.translations.enable")) {
      return;
    }

    /**
     * Which phase a language download is in.
     *
     * @typedef {"downloaded" | "loading" | "uninstalled"} DownloadPhase
     */


    // Immediately show the group so that the async load of the component does
    // not cause the layout to jump. The group will be empty initially.
    document.getElementById("translationsGroup").hidden = false;

    class TranslationsState {
      /**
       * The fully initialized state.
       *
       * @param {Object} supportedLanguages
       * @param {Array<{ langTag: string, displayName: string}} languageList
       * @param {Map<string, DownloadPhase>} downloadPhases
       */

      constructor(supportedLanguages, languageList, downloadPhases) {
        this.supportedLanguages = supportedLanguages;
        this.languageList = languageList;
        this.downloadPhases = downloadPhases;
      }

      /**
       * Handles all of the async initialization logic.
       */

      static async create() {
        const supportedLanguages =
          await TranslationsParent.getSupportedLanguages();
        const languageList =
          TranslationsParent.getLanguageList(supportedLanguages);
        const downloadPhases =
          await TranslationsState.createDownloadPhases(languageList);

        if (supportedLanguages.languagePairs.length === 0) {
          throw new Error(
            "The supported languages list was empty. RemoteSettings may not be available at the moment."
          );
        }

        return new TranslationsState(
          supportedLanguages,
          languageList,
          downloadPhases
        );
      }

      /**
       * Determine the download phase of each language file.
       *
       * @param {Array<{ langTag: string, displayName: string}} languageList.
       * @returns {Map<string, DownloadPhase>} Map the language tag to whether it is downloaded.
       */

      static async createDownloadPhases(languageList) {
        const downloadPhases = new Map();
        for (const { langTag } of languageList) {
          downloadPhases.set(
            langTag,
            (await TranslationsParent.hasAllFilesForLanguage(langTag))
              ? "downloaded"
              : "uninstalled"
          );
        }
        return downloadPhases;
      }
    }

    class TranslationsView {
      /** @type {Map<string, XULButton>} */
      deleteButtons = new Map();
      /** @type {Map<string, XULButton>} */
      downloadButtons = new Map();

      /**
       * @param {TranslationsState} state
       */

      constructor(state) {
        this.state = state;
        this.elements = {
          settingsButton: document.getElementById(
            "translations-manage-settings-button"
          ),
          installList: document.getElementById(
            "translations-manage-install-list"
          ),
          installAll: document.getElementById(
            "translations-manage-install-all"
          ),
          deleteAll: document.getElementById("translations-manage-delete-all"),
          error: document.getElementById("translations-manage-error"),
        };
        this.setup();
      }

      setup() {
        this.buildLanguageList();

        this.elements.settingsButton.addEventListener(
          "command",
          gMainPane.showTranslationsSettings
        );
        this.elements.installAll.addEventListener(
          "command",
          this.handleInstallAll
        );
        this.elements.deleteAll.addEventListener(
          "command",
          this.handleDeleteAll
        );
      }

      handleInstallAll = async () => {
        this.hideError();
        this.disableButtons(true);
        try {
          await TranslationsParent.downloadAllFiles();
          this.markAllDownloadPhases("downloaded");
        } catch (error) {
          TranslationsView.showError(
            "translations-manage-error-download",
            error
          );
          await this.reloadDownloadPhases();
          this.updateAllButtons();
        }
        this.disableButtons(false);
      };

      handleDeleteAll = async () => {
        this.hideError();
        this.disableButtons(true);
        try {
          await TranslationsParent.deleteAllLanguageFiles();
          this.markAllDownloadPhases("uninstalled");
        } catch (error) {
          TranslationsView.showError("translations-manage-error-remove", error);
          // The download phases are invalidated with the error and must be reloaded.
          await this.reloadDownloadPhases();
          console.error(error);
        }
        this.disableButtons(false);
      };

      /**
       * @param {string} langTag
       * @returns {Function}
       */

      getDownloadButtonHandler(langTag) {
        return async () => {
          this.hideError();
          this.updateDownloadPhase(langTag, "loading");
          try {
            await TranslationsParent.downloadLanguageFiles(langTag);
            this.updateDownloadPhase(langTag, "downloaded");
          } catch (error) {
            TranslationsView.showError(
              "translations-manage-error-download",
              error
            );
            this.updateDownloadPhase(langTag, "uninstalled");
          }
        };
      }

      /**
       * @param {string} langTag
       * @returns {Function}
       */

      getDeleteButtonHandler(langTag) {
        return async () => {
          this.hideError();
          this.updateDownloadPhase(langTag, "loading");
          try {
            await TranslationsParent.deleteLanguageFiles(langTag);
            this.updateDownloadPhase(langTag, "uninstalled");
          } catch (error) {
            TranslationsView.showError(
              "translations-manage-error-remove",
              error
            );
            // The download phases are invalidated with the error and must be reloaded.
            await this.reloadDownloadPhases();
          }
        };
      }

      buildLanguageList() {
        const listFragment = document.createDocumentFragment();

        for (const { langTag, displayName } of this.state.languageList) {
          const hboxRow = document.createXULElement("hbox");
          hboxRow.classList.add("translations-manage-language");

          const languageLabel = document.createXULElement("label");
          languageLabel.textContent = displayName; // The display name is already localized.

          const downloadButton = document.createXULElement("button");
          const deleteButton = document.createXULElement("button");

          downloadButton.addEventListener(
            "command",
            this.getDownloadButtonHandler(langTag)
          );
          deleteButton.addEventListener(
            "command",
            this.getDeleteButtonHandler(langTag)
          );

          document.l10n.setAttributes(
            downloadButton,
            "translations-manage-language-download-button"
          );
          document.l10n.setAttributes(
            deleteButton,
            "translations-manage-language-remove-button"
          );

          downloadButton.hidden = true;
          deleteButton.hidden = true;

          this.deleteButtons.set(langTag, deleteButton);
          this.downloadButtons.set(langTag, downloadButton);

          hboxRow.appendChild(languageLabel);
          hboxRow.appendChild(downloadButton);
          hboxRow.appendChild(deleteButton);
          listFragment.appendChild(hboxRow);
        }
        this.updateAllButtons();
        this.elements.installList.appendChild(listFragment);
        this.elements.installList.hidden = false;
      }

      /**
       * Update the DownloadPhase for a single langTag.
       * @param {string} langTag
       * @param {DownloadPhase} downloadPhase
       */

      updateDownloadPhase(langTag, downloadPhase) {
        this.state.downloadPhases.set(langTag, downloadPhase);
        this.updateButton(langTag, downloadPhase);
        this.updateHeaderButtons();
      }

      /**
       * Recreates the download map when the state is invalidated.
       */

      async reloadDownloadPhases() {
        this.state.downloadPhases =
          await TranslationsState.createDownloadPhases(this.state.languageList);
        this.updateAllButtons();
      }

      /**
       * Set all the downloads.
       * @param {DownloadPhase} downloadPhase
       */

      markAllDownloadPhases(downloadPhase) {
        const { downloadPhases } = this.state;
        for (const key of downloadPhases.keys()) {
          downloadPhases.set(key, downloadPhase);
        }
        this.updateAllButtons();
      }

      /**
       * If all languages are downloaded, or no languages are downloaded then
       * the visibility of the buttons need to change.
       */

      updateHeaderButtons() {
        let allDownloaded = true;
        let allUninstalled = true;
        for (const downloadPhase of this.state.downloadPhases.values()) {
          if (downloadPhase === "loading") {
            // Don't count loading towards this calculation.
            continue;
          }
          allDownloaded &&= downloadPhase === "downloaded";
          allUninstalled &&= downloadPhase === "uninstalled";
        }

        this.elements.installAll.hidden = allDownloaded;
        this.elements.deleteAll.hidden = allUninstalled;
      }

      /**
       * Update the buttons according to their download state.
       */

      updateAllButtons() {
        this.updateHeaderButtons();
        for (const [langTag, downloadPhase] of this.state.downloadPhases) {
          this.updateButton(langTag, downloadPhase);
        }
      }

      /**
       * @param {string} langTag
       * @param {DownloadPhase} downloadPhase
       */

      updateButton(langTag, downloadPhase) {
        const downloadButton = this.downloadButtons.get(langTag);
        const deleteButton = this.deleteButtons.get(langTag);
        switch (downloadPhase) {
          case "downloaded":
            downloadButton.hidden = true;
            deleteButton.hidden = false;
            downloadButton.removeAttribute("disabled");
            break;
          case "uninstalled":
            downloadButton.hidden = false;
            deleteButton.hidden = true;
            downloadButton.removeAttribute("disabled");
            break;
          case "loading":
            downloadButton.hidden = false;
            deleteButton.hidden = true;
            downloadButton.setAttribute("disabled"true);
            break;
        }
      }

      /**
       * @param {boolean} isDisabled
       */

      disableButtons(isDisabled) {
        this.elements.installAll.disabled = isDisabled;
        this.elements.deleteAll.disabled = isDisabled;
        for (const button of this.downloadButtons.values()) {
          button.disabled = isDisabled;
        }
        for (const button of this.deleteButtons.values()) {
          button.disabled = isDisabled;
        }
      }

      /**
       * This method is static in case an error happens during the creation of the
       * TranslationsState.
       *
       * @param {string} l10nId
       * @param {Error} error
       */

      static showError(l10nId, error) {
        console.error(error);
        const errorMessage = document.getElementById(
          "translations-manage-error"
        );
        errorMessage.hidden = false;
        document.l10n.setAttributes(errorMessage, l10nId);
      }

      hideError() {
        this.elements.error.hidden = true;
      }
    }

    TranslationsState.create().then(
      state => {
        new TranslationsView(state);
      },
      error => {
        // This error can happen when a user is not connected to the internet, or
        // RemoteSettings is down for some reason.
        TranslationsView.showError("translations-manage-error-list", error);
      }
    );
  },

  initPrimaryBrowserLanguageUI() {
    // This will register the "command" listener.
    let menulist = document.getElementById("primaryBrowserLocale");
    new SelectionChangedMenulist(menulist, event => {
      gMainPane.onPrimaryBrowserLanguageMenuChange(event);
    });

    gMainPane.updatePrimaryBrowserLanguageUI(Services.locale.appLocaleAsBCP47);
  },

  /**
   * Update the available list of locales and select the locale that the user
   * is "selecting". This could be the currently requested locale or a locale
   * that the user would like to switch to after confirmation.
   *
   * @param {string} selected - The selected BCP 47 locale.
   */

  async updatePrimaryBrowserLanguageUI(selected) {
    let available = await LangPackMatcher.getAvailableLocales();
    let localeNames = Services.intl.getLocaleDisplayNames(
      undefined,
      available,
      { preferNative: true }
    );
    let locales = available.map((code, i) => ({ code, name: localeNames[i] }));
    locales.sort((a, b) => a.name > b.name);

    let fragment = document.createDocumentFragment();
    for (let { code, name } of locales) {
      let menuitem = document.createXULElement("menuitem");
      menuitem.setAttribute("value", code);
      menuitem.setAttribute("label", name);
      fragment.appendChild(menuitem);
    }

    // Add an option to search for more languages if downloading is supported.
    if (Services.prefs.getBoolPref("intl.multilingual.downloadEnabled")) {
      let menuitem = document.createXULElement("menuitem");
      menuitem.id = "primaryBrowserLocaleSearch";
      menuitem.setAttribute(
        "label",
        await document.l10n.formatValue("browser-languages-search")
      );
      menuitem.setAttribute("value""search");
      fragment.appendChild(menuitem);
    }

    let menulist = document.getElementById("primaryBrowserLocale");
    let menupopup = menulist.querySelector("menupopup");
    menupopup.textContent = "";
    menupopup.appendChild(fragment);
    menulist.value = selected;

    document.getElementById("browserLanguagesBox").hidden = false;
  },

  /* Show the confirmation message bar to allow a restart into the new locales. */
  async showConfirmLanguageChangeMessageBar(locales) {
    let messageBar = document.getElementById("confirmBrowserLanguage");

    // Get the bundle for the new locale.
    let newBundle = getBundleForLocales(locales);

    // Find the messages and labels.
    let messages = await Promise.all(
      [newBundle, document.l10n].map(async bundle =>
        bundle.formatValue("confirm-browser-language-change-description")
      )
    );
    let buttonLabels = await Promise.all(
      [newBundle, document.l10n].map(async bundle =>
        bundle.formatValue("confirm-browser-language-change-button")
      )
    );

    // If both the message and label are the same, just include one row.
    if (messages[0] == messages[1] && buttonLabels[0] == buttonLabels[1]) {
      messages.pop();
      buttonLabels.pop();
    }

    let contentContainer = messageBar.querySelector(
      ".message-bar-content-container"
    );
    contentContainer.textContent = "";

    for (let i = 0; i < messages.length; i++) {
      let messageContainer = document.createXULElement("hbox");
      messageContainer.classList.add("message-bar-content");
      messageContainer.style.flex = "1 50%";
      messageContainer.setAttribute("align""center");

      let description = document.createXULElement("description");
      description.classList.add("message-bar-description");

      if (i == 0 && Services.intl.getScriptDirection(locales[0]) === "rtl") {
        description.classList.add("rtl-locale");
      }
      description.setAttribute("flex""1");
      description.textContent = messages[i];
      messageContainer.appendChild(description);

      let button = document.createXULElement("button");
      button.addEventListener(
        "command",
        gMainPane.confirmBrowserLanguageChange
      );
      button.classList.add("message-bar-button");
      button.setAttribute("locales", locales.join(","));
      button.setAttribute("label", buttonLabels[i]);
      messageContainer.appendChild(button);

      contentContainer.appendChild(messageContainer);
    }

    messageBar.hidden = false;
    gMainPane.selectedLocalesForRestart = locales;
  },

  hideConfirmLanguageChangeMessageBar() {
    let messageBar = document.getElementById("confirmBrowserLanguage");
    messageBar.hidden = true;
    let contentContainer = messageBar.querySelector(
      ".message-bar-content-container"
    );
    contentContainer.textContent = "";
    gMainPane.requestingLocales = null;
  },

  /* Confirm the locale change and restart the browser in the new locale. */
  confirmBrowserLanguageChange(event) {
    let localesString = (event.target.getAttribute("locales") || "").trim();
    if (!localesString || !localesString.length) {
      return;
    }
    let locales = localesString.split(",");
    Services.locale.requestedLocales = locales;

    // Record the change in telemetry before we restart.
    gMainPane.recordBrowserLanguagesTelemetry("apply");

    // Restart with the new locale.
    let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(
      Ci.nsISupportsPRBool
    );
    Services.obs.notifyObservers(
      cancelQuit,
      "quit-application-requested",
      "restart"
    );
    if (!cancelQuit.data) {
      Services.startup.quit(
        Services.startup.eAttemptQuit | Services.startup.eRestart
      );
    }
  },

  /* Show or hide the confirm change message bar based on the new locale. */
  onPrimaryBrowserLanguageMenuChange(event) {
    let locale = event.target.value;

    if (locale == "search") {
      gMainPane.showBrowserLanguagesSubDialog({ search: true });
      return;
    } else if (locale == Services.locale.appLocaleAsBCP47) {
      this.hideConfirmLanguageChangeMessageBar();
      return;
    }

    let newLocales = Array.from(
      new Set([locale, ...Services.locale.requestedLocales]).values()
    );

    gMainPane.recordBrowserLanguagesTelemetry("reorder");

    switch (gMainPane.getLanguageSwitchTransitionType(newLocales)) {
      case "requires-restart":
        // Prepare to change the locales, as they were different.
        gMainPane.showConfirmLanguageChangeMessageBar(newLocales);
        gMainPane.updatePrimaryBrowserLanguageUI(newLocales[0]);
        break;
      case "live-reload":
        Services.locale.requestedLocales = newLocales;
        gMainPane.updatePrimaryBrowserLanguageUI(
          Services.locale.appLocaleAsBCP47
        );
        gMainPane.hideConfirmLanguageChangeMessageBar();
        break;
      case "locales-match":
        // They matched, so we can reset the UI.
        gMainPane.updatePrimaryBrowserLanguageUI(
          Services.locale.appLocaleAsBCP47
        );
        gMainPane.hideConfirmLanguageChangeMessageBar();
        break;
      default:
        throw new Error("Unhandled transition type.");
    }
  },

  /**
   * Takes as newZoom a floating point value representing the
   * new default zoom. This value should not be a string, and
   * should not carry a percentage sign/other localisation
   * characteristics.
   */

  handleDefaultZoomChange(newZoom) {
    let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
      Ci.nsIContentPrefService2
    );
    let nonPrivateLoadContext = Cu.createLoadContext();
    /* Because our setGlobal function takes in a browsing context, and
     * because we want to keep this property consistent across both private
     * and non-private contexts, we crate a non-private context and use that
     * to set the property, regardless of our actual context.
     */


    let win = window.browsingContext.topChromeWindow;
    cps2.setGlobal(win.FullZoom.name, newZoom, nonPrivateLoadContext);
  },

  onBrowserRestoreSessionChange(event) {
    const value = event.target.checked;
    const startupPref = Preferences.get("browser.startup.page");
    let newValue;

    if (value) {
      // We need to restore the blank homepage setting in our other pref
      if (startupPref.value === this.STARTUP_PREF_BLANK) {
        HomePage.safeSet("about:blank");
      }
      newValue = this.STARTUP_PREF_RESTORE_SESSION;
    } else {
      newValue = this.STARTUP_PREF_HOMEPAGE;
    }
    startupPref.value = newValue;
  },

  async onWindowsLaunchOnLoginChange(event) {
    if (AppConstants.platform !== "win") {
      return;
    }
    if (event.target.checked) {
      // windowsLaunchOnLogin has been checked: create registry key or shortcut
      // The shortcut is created with the same AUMID as Firefox itself. However,
      // this is not set during browser tests and the fallback of checking the
      // registry fails. As such we pass an arbitrary AUMID for the purpose
      // of testing.
      await WindowsLaunchOnLogin.createLaunchOnLogin();
      Services.prefs.setBoolPref(
        "browser.startup.windowsLaunchOnLogin.disableLaunchOnLoginPrompt",
        true
      );
    } else {
      // windowsLaunchOnLogin has been unchecked: delete registry key and shortcut
      await WindowsLaunchOnLogin.removeLaunchOnLogin();
    }
  },

  // TABS

  /*
   * Preferences:
   *
   * browser.link.open_newwindow - int
   *   Determines where links targeting new windows should open.
   *   Values:
   *     1 - Open in the current window or tab.
   *     2 - Open in a new window.
   *     3 - Open in a new tab in the most recent window.
   * browser.tabs.loadInBackground - bool
   *   True - Whether browser should switch to a new tab opened from a link.
   * browser.tabs.warnOnClose - bool
   *   True - If when closing a window with multiple tabs the user is warned and
   *          allowed to cancel the action, false to just close the window.
   * browser.warnOnQuitShortcut - bool
   *   True - If the keyboard shortcut (Ctrl/Cmd+Q) is pressed, the user should
   *          be warned, false to just quit without prompting.
   * browser.tabs.warnOnOpen - bool
   *   True - Whether the user should be warned when trying to open a lot of
   *          tabs at once (e.g. a large folder of bookmarks), allowing to
   *          cancel the action.
   * browser.taskbar.previews.enable - bool
   *   True - Tabs are to be shown in Windows 7 taskbar.
   *   False - Only the window is to be shown in Windows 7 taskbar.
   */


  /**
   * Determines where a link which opens a new window will open.
   *
   * @returns |true| if such links should be opened in new tabs
   */

  readLinkTarget() {
    var openNewWindow = Preferences.get("browser.link.open_newwindow");
    return openNewWindow.value != 2;
  },

  /**
   * Determines where a link which opens a new window will open.
   *
   * @returns 2 if such links should be opened in new windows,
   *          3 if such links should be opened in new tabs
   */

  writeLinkTarget() {
    var linkTargeting = document.getElementById("linkTargeting");
    return linkTargeting.checked ? 3 : 2;
  },
  /*
   * Preferences:
   *
   * browser.shell.checkDefault
   * - true if a default-browser check (and prompt to make it so if necessary)
   *   occurs at startup, false otherwise
   */


  /**
   * Show button for setting browser as default browser or information that
   * browser is already the default browser.
   */

  updateSetDefaultBrowser() {
    if (AppConstants.HAVE_SHELL_SERVICE) {
      let shellSvc = getShellService();
      let defaultBrowserBox = document.getElementById("defaultBrowserBox");
      let isInFlatpak = gGIOService?.isRunningUnderFlatpak;
      // Flatpak does not support setting nor detection of default browser
      if (!shellSvc || isInFlatpak) {
        defaultBrowserBox.hidden = true;
        return;
      }
      let isDefault = shellSvc.isDefaultBrowser(falsetrue);
      let setDefaultPane = document.getElementById("setDefaultPane");
      setDefaultPane.classList.toggle("is-default", isDefault);
      let alwaysCheck = document.getElementById("alwaysCheckDefault");
      let alwaysCheckPref = Preferences.get(
        "browser.shell.checkDefaultBrowser"
      );
      alwaysCheck.disabled = alwaysCheckPref.locked || isDefault;
    }
  },

  /**
   * Set browser as the operating system default browser.
   */

  async setDefaultBrowser() {
    if (AppConstants.HAVE_SHELL_SERVICE) {
      let alwaysCheckPref = Preferences.get(
        "browser.shell.checkDefaultBrowser"
      );
      alwaysCheckPref.value = true;

      // Reset exponential backoff delay time in order to do visual update in pollForDefaultBrowser.
      this._backoffIndex = 0;

      let shellSvc = getShellService();
      if (!shellSvc) {
        return;
      }

      // Disable the set default button, so that the user doesn't try to hit it again
      // while awaiting on setDefaultBrowser
      let setDefaultButton = document.getElementById("setDefaultButton");
      setDefaultButton.disabled = true;

      try {
        await shellSvc.setDefaultBrowser(false);
      } catch (ex) {
        console.error(ex);
        return;
      } finally {
        // Make sure to re-enable the default button when we're finished, regardless of the outcome
        setDefaultButton.disabled = false;
      }

      let isDefault = shellSvc.isDefaultBrowser(falsetrue);
      let setDefaultPane = document.getElementById("setDefaultPane");
      setDefaultPane.classList.toggle("is-default", isDefault);
    }
  },

  /**
   * Shows a dialog in which the preferred language for web content may be set.
   */

  showLanguages() {
    gSubDialog.open(
      "chrome://browser/content/preferences/dialogs/languages.xhtml"
    );
  },

  recordBrowserLanguagesTelemetry(method, value = null) {
    Glean.intlUiBrowserLanguage[method + "Main"].record(
      value ? { value } : undefined
    );
  },

  /**
   * Open the browser languages sub dialog in either the normal mode, or search mode.
   * The search mode is only available from the menu to change the primary browser
   * language.
   *
   * @param {{ search: boolean }}
   */

  showBrowserLanguagesSubDialog({ search }) {
    // Record the telemetry event with an id to associate related actions.
    let telemetryId = parseInt(
      Services.telemetry.msSinceProcessStart(),
      10
    ).toString();
    let method = search ? "search" : "manage";
    gMainPane.recordBrowserLanguagesTelemetry(method, telemetryId);

    let opts = {
      selectedLocalesForRestart: gMainPane.selectedLocalesForRestart,
      search,
      telemetryId,
    };
    gSubDialog.open(
      "chrome://browser/content/preferences/dialogs/browserLanguages.xhtml",
      { closingCallback: this.browserLanguagesClosed },
      opts
    );
  },

  /**
   * Determine the transition strategy for switching the locale based on prefs
   * and the switched locales.
   *
   * @param {Array<string>} newLocales - List of BCP 47 locale identifiers.
   * @returns {"locales-match" | "requires-restart" | "live-reload"}
   */

  getLanguageSwitchTransitionType(newLocales) {
    const { appLocalesAsBCP47 } = Services.locale;
    if (appLocalesAsBCP47.join(",") === newLocales.join(",")) {
      // The selected locales match, the order matters.
      return "locales-match";
    }

    if (Services.prefs.getBoolPref("intl.multilingual.liveReload")) {
      if (
        Services.intl.getScriptDirection(newLocales[0]) !==
          Services.intl.getScriptDirection(appLocalesAsBCP47[0]) &&
        !Services.prefs.getBoolPref("intl.multilingual.liveReloadBidirectional")
      ) {
        // Bug 1750852: The directionality of the text changed, which requires a restart
        // until the quality of the switch can be improved.
        return "requires-restart";
      }

      return "live-reload";
    }

    return "requires-restart";
  },

  /* Show or hide the confirm change message bar based on the updated ordering. */
  browserLanguagesClosed() {
    // When the subdialog is closed, settings are stored on gBrowserLanguagesDialog.
    // The next time the dialog is opened, a new gBrowserLanguagesDialog is created.
    let { selected } = this.gBrowserLanguagesDialog;

    this.gBrowserLanguagesDialog.recordTelemetry(
      selected ? "accept" : "cancel"
    );

    if (!selected) {
      // No locales were selected. Cancel the operation.
      return;
    }

    // Track how often locale fallback order is changed.
    // Drop the first locale and filter to only include the overlapping set
    const prevLocales = Services.locale.requestedLocales.filter(
      lc => selected.indexOf(lc) > 0
    );
    const newLocales = selected.filter(
      (lc, i) => i > 0 && prevLocales.includes(lc)
    );
    if (prevLocales.some((lc, i) => newLocales[i] != lc)) {
      this.gBrowserLanguagesDialog.recordTelemetry("setFallback");
    }

    switch (gMainPane.getLanguageSwitchTransitionType(selected)) {
      case "requires-restart":
        gMainPane.showConfirmLanguageChangeMessageBar(selected);
        gMainPane.updatePrimaryBrowserLanguageUI(selected[0]);
        break;
      case "live-reload":
        Services.locale.requestedLocales = selected;

        gMainPane.updatePrimaryBrowserLanguageUI(
          Services.locale.appLocaleAsBCP47
        );
        gMainPane.hideConfirmLanguageChangeMessageBar();
        break;
--> --------------------

--> maximum size reached

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

Messung V0.5
C=93 H=97 G=94

¤ Dauer der Verarbeitung: 0.24 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.