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

Quelle  browser-siteProtections.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/. */


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

ChromeUtils.defineESModuleGetters(this, {
  ContentBlockingAllowList:
    "resource://gre/modules/ContentBlockingAllowList.sys.mjs",
  ReportBrokenSite: "resource:///modules/ReportBrokenSite.sys.mjs",
  SpecialMessageActions:
    "resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
});

XPCOMUtils.defineLazyServiceGetter(
  this,
  "TrackingDBService",
  "@mozilla.org/tracking-db-service;1",
  "nsITrackingDBService"
);

/**
 * Represents a protection category shown in the protections UI. For the most
 * common categories we can directly instantiate this category. Some protections
 * categories inherit from this class and overwrite some of its members.
 */

class ProtectionCategory {
  /**
   * Creates a protection category.
   * @param {string} id - Identifier of the category. Used to query the category
   * UI elements in the DOM.
   * @param {Object} options - Category options.
   * @param {string} options.prefEnabled - ID of pref which controls the
   * category enabled state.
   * @param {Object} flags - Flags for this category to look for in the content
   * blocking event and content blocking log.
   * @param {Number} [flags.load] - Load flag for this protection category. If
   * omitted, we will never match a isAllowing check for this category.
   * @param {Number} [flags.block] - Block flag for this protection category. If
   * omitted, we will never match a isBlocking check for this category.
   * @param {Number} [flags.shim] - Shim flag for this protection category. This
   * flag is set if we replaced tracking content with a non-tracking shim
   * script.
   * @param {Number} [flags.allow] - Allow flag for this protection category.
   * This flag is set if we explicitly allow normally blocked tracking content.
   * The webcompat extension can do this if it needs to unblock content on user
   * opt-in.
   */

  constructor(
    id,
    { prefEnabled },
    {
      load,
      block,
      shim = Ci.nsIWebProgressListener.STATE_REPLACED_TRACKING_CONTENT,
      allow = Ci.nsIWebProgressListener.STATE_ALLOWED_TRACKING_CONTENT,
    }
  ) {
    this._id = id;
    this.prefEnabled = prefEnabled;

    this._flags = { load, block, shim, allow };

    if (
      Services.prefs.getPrefType(this.prefEnabled) == Services.prefs.PREF_BOOL
    ) {
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "_enabled",
        this.prefEnabled,
        false,
        this.updateCategoryItem.bind(this)
      );
    }

    MozXULElement.insertFTLIfNeeded("browser/siteProtections.ftl");

    ChromeUtils.defineLazyGetter(this"subView", () =>
      document.getElementById(`protections-popup-${this._id}View`)
    );

    ChromeUtils.defineLazyGetter(this"subViewHeading", () =>
      document.getElementById(`protections-popup-${this._id}View-heading`)
    );

    ChromeUtils.defineLazyGetter(this"subViewList", () =>
      document.getElementById(`protections-popup-${this._id}View-list`)
    );

    ChromeUtils.defineLazyGetter(this"subViewShimAllowHint", () =>
      document.getElementById(
        `protections-popup-${this._id}View-shim-allow-hint`
      )
    );

    ChromeUtils.defineLazyGetter(this"isWindowPrivate", () =>
      PrivateBrowsingUtils.isWindowPrivate(window)
    );
  }

  // Child classes may override these to do init / teardown. We expect them to
  // be called when the protections panel is initialized or destroyed.
  init() {}
  uninit() {}

  // Some child classes may overide this getter.
  get enabled() {
    return this._enabled;
  }

  /**
   * Get the category item associated with this protection from the main
   * protections panel.
   * @returns {xul:toolbarbutton|undefined} - Item or undefined if the panel is
   * not yet initialized.
   */

  get categoryItem() {
    // We don't use defineLazyGetter for the category item, since it may be null
    // on first access.
    return (
      this._categoryItem ||
      (this._categoryItem = document.getElementById(
        `protections-popup-category-${this._id}`
      ))
    );
  }

  /**
   * Defaults to enabled state. May be overridden by child classes.
   * @returns {boolean} - Whether the protection is set to block trackers.
   */

  get blockingEnabled() {
    return this.enabled;
  }

  /**
   * Update the category item state in the main view of the protections panel.
   * Determines whether the category is set to block trackers.
   * @returns {boolean} - true if the state has been updated, false if the
   * protections popup has not been initialized yet.
   */

  updateCategoryItem() {
    // Can't get `this.categoryItem` without the popup. Using the popup instead
    // of `this.categoryItem` to guard access, because the category item getter
    // can trigger bug 1543537. If there's no popup, we'll be called again the
    // first time the popup shows.
    if (!gProtectionsHandler._protectionsPopup) {
      return false;
    }
    this.categoryItem.classList.toggle("blocked"this.enabled);
    this.categoryItem.classList.toggle("subviewbutton-nav"this.enabled);
    return true;
  }

  /**
   * Update the category sub view that is shown when users click on the category
   * button.
   */

  async updateSubView() {
    let { items, anyShimAllowed } = await this._generateSubViewListItems();
    this.subViewShimAllowHint.hidden = !anyShimAllowed;

    this.subViewList.textContent = "";
    this.subViewList.append(items);
    const isBlocking =
      this.blockingEnabled && !gProtectionsHandler.hasException;
    let l10nId;
    switch (this._id) {
      case "cryptominers":
        l10nId = isBlocking
          ? "protections-blocking-cryptominers"
          : "protections-not-blocking-cryptominers";
        break;
      case "fingerprinters":
        l10nId = isBlocking
          ? "protections-blocking-fingerprinters"
          : "protections-not-blocking-fingerprinters";
        break;
      case "socialblock":
        l10nId = isBlocking
          ? "protections-blocking-social-media-trackers"
          : "protections-not-blocking-social-media-trackers";
        break;
    }
    if (l10nId) {
      document.l10n.setAttributes(this.subView, l10nId);
    }
  }

  /**
   * Create a list of items, each representing a tracker.
   * @returns {Object} result - An object containing the results.
   * @returns {HTMLDivElement[]} result.items - Generated tracker items. May be
   * empty.
   * @returns {boolean} result.anyShimAllowed - Flag indicating if any of the
   * items have been unblocked by a shim script.
   */

  async _generateSubViewListItems() {
    let contentBlockingLog = gBrowser.selectedBrowser.getContentBlockingLog();
    contentBlockingLog = JSON.parse(contentBlockingLog);
    let anyShimAllowed = false;

    let fragment = document.createDocumentFragment();
    for (let [origin, actions] of Object.entries(contentBlockingLog)) {
      let { item, shimAllowed } = await this._createListItem(origin, actions);
      if (!item) {
        continue;
      }
      anyShimAllowed = anyShimAllowed || shimAllowed;
      fragment.appendChild(item);
    }

    return {
      items: fragment,
      anyShimAllowed,
    };
  }

  /**
   * Create a DOM item representing a tracker.
   * @param {string} origin - Origin of the tracker.
   * @param {Array} actions - Array of actions from the content blocking log
   * associated with the tracking origin.
   * @returns {Object} result - An object containing the results.
   * @returns {HTMLDListElement} [options.item] - Generated item or null if we
   * don't have an item for this origin based on the actions log.
   * @returns {boolean} options.shimAllowed - Flag indicating whether the
   * tracking origin was allowed by a shim script.
   */

  _createListItem(origin, actions) {
    let isAllowed = actions.some(
      ([state]) => this.isAllowing(state) && !this.isShimming(state)
    );
    let isDetected =
      isAllowed || actions.some(([state]) => this.isBlocking(state));

    if (!isDetected) {
      return {};
    }

    // Create an item to hold the origin label and shim allow indicator. Using
    // an html element here, so we can use CSS flex, which handles the label
    // overflow in combination with the icon correctly.
    let listItem = document.createElementNS(
      "http://www.w3.org/1999/xhtml",
      "div"
    );
    listItem.className = "protections-popup-list-item";
    listItem.classList.toggle("allowed", isAllowed);

    let label = document.createXULElement("label");
    // Repeat the host in the tooltip in case it's too long
    // and overflows in our panel.
    label.tooltipText = origin;
    label.value = origin;
    label.className = "protections-popup-list-host-label";
    label.setAttribute("crop""end");
    listItem.append(label);

    // Determine whether we should show a shim-allow indicator for this item.
    let shimAllowed = actions.some(([flag]) => flag == this._flags.allow);
    if (shimAllowed) {
      listItem.append(this._getShimAllowIndicator());
    }

    return { item: listItem, shimAllowed };
  }

  /**
   * Create an indicator icon for marking origins that have been allowed by a
   * shim script.
   * @returns {HTMLImageElement} - Created element.
   */

  _getShimAllowIndicator() {
    let allowIndicator = document.createXULElement("image");
    document.l10n.setAttributes(
      allowIndicator,
      "protections-panel-shim-allowed-indicator"
    );
    allowIndicator.classList.add(
      "protections-popup-list-host-shim-allow-indicator"
    );
    return allowIndicator;
  }

  /**
   * @param {Number} state - Content blocking event flags.
   * @returns {boolean} - Whether the protection has blocked a tracker.
   */

  isBlocking(state) {
    return (state & this._flags.block) != 0;
  }

  /**
   * @param {Number} state - Content blocking event flags.
   * @returns {boolean} - Whether the protection has allowed a tracker.
   */

  isAllowing(state) {
    return (state & this._flags.load) != 0;
  }

  /**
   * @param {Number} state - Content blocking event flags.
   * @returns {boolean} - Whether the protection has detected (blocked or
   * allowed) a tracker.
   */

  isDetected(state) {
    return this.isBlocking(state) || this.isAllowing(state);
  }

  /**
   * @param {Number} state - Content blocking event flags.
   * @returns {boolean} - Whether the protections has allowed a tracker that
   * would have normally been blocked.
   */

  isShimming(state) {
    return (state & this._flags.shim) != 0 && this.isAllowing(state);
  }
}

let Fingerprinting =
  new (class FingerprintingProtection extends ProtectionCategory {
    constructor() {
      super(
        "fingerprinters",
        {
          prefEnabled: "privacy.trackingprotection.fingerprinting.enabled",
        },
        {
          load: Ci.nsIWebProgressListener.STATE_LOADED_FINGERPRINTING_CONTENT,
          block: Ci.nsIWebProgressListener.STATE_BLOCKED_FINGERPRINTING_CONTENT,
          shim: Ci.nsIWebProgressListener.STATE_REPLACED_FINGERPRINTING_CONTENT,
          allow: Ci.nsIWebProgressListener.STATE_ALLOWED_FINGERPRINTING_CONTENT,
        }
      );

      this.prefFPPEnabled = "privacy.fingerprintingProtection";
      this.prefFPPEnabledInPrivateWindows =
        "privacy.fingerprintingProtection.pbmode";

      this.enabledFPB = false;
      this.enabledFPPGlobally = false;
      this.enabledFPPInPrivateWindows = false;
    }

    init() {
      this.updateEnabled();

      Services.prefs.addObserver(this.prefEnabled, this);
      Services.prefs.addObserver(this.prefFPPEnabled, this);
      Services.prefs.addObserver(this.prefFPPEnabledInPrivateWindows, this);
    }

    uninit() {
      Services.prefs.removeObserver(this.prefEnabled, this);
      Services.prefs.removeObserver(this.prefFPPEnabled, this);
      Services.prefs.removeObserver(this.prefFPPEnabledInPrivateWindows, this);
    }

    updateEnabled() {
      this.enabledFPB = Services.prefs.getBoolPref(this.prefEnabled);
      this.enabledFPPGlobally = Services.prefs.getBoolPref(this.prefFPPEnabled);
      this.enabledFPPInPrivateWindows = Services.prefs.getBoolPref(
        this.prefFPPEnabledInPrivateWindows
      );
    }

    observe() {
      this.updateEnabled();
      this.updateCategoryItem();
    }

    get enabled() {
      return (
        this.enabledFPB ||
        this.enabledFPPGlobally ||
        (this.isWindowPrivate && this.enabledFPPInPrivateWindows)
      );
    }

    isBlocking(state) {
      let blockFlag = this._flags.block;

      // We only consider the suspicious fingerprinting flag if the
      // fingerprinting protection is enabled in the context.
      if (
        this.enabledFPPGlobally ||
        (this.isWindowPrivate && this.enabledFPPInPrivateWindows)
      ) {
        blockFlag |=
          Ci.nsIWebProgressListener.STATE_BLOCKED_SUSPICIOUS_FINGERPRINTING;
      }

      return (state & blockFlag) != 0;
    }

    // TODO (Bug 1864914): Consider showing suspicious fingerprinting as allowed
    // when the fingerprinting protection is disabled.
  })();

let Cryptomining = new ProtectionCategory(
  "cryptominers",
  {
    prefEnabled: "privacy.trackingprotection.cryptomining.enabled",
  },
  {
    load: Ci.nsIWebProgressListener.STATE_LOADED_CRYPTOMINING_CONTENT,
    block: Ci.nsIWebProgressListener.STATE_BLOCKED_CRYPTOMINING_CONTENT,
  }
);

let TrackingProtection =
  new (class TrackingProtection extends ProtectionCategory {
    constructor() {
      super(
        "trackers",
        {
          prefEnabled: "privacy.trackingprotection.enabled",
        },
        {
          load: null,
          block:
            Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT |
            Ci.nsIWebProgressListener.STATE_BLOCKED_EMAILTRACKING_CONTENT,
        }
      );

      this.prefEnabledInPrivateWindows =
        "privacy.trackingprotection.pbmode.enabled";
      this.prefTrackingTable = "urlclassifier.trackingTable";
      this.prefTrackingAnnotationTable =
        "urlclassifier.trackingAnnotationTable";
      this.prefAnnotationsLevel2Enabled =
        "privacy.annotate_channels.strict_list.enabled";
      this.prefEmailTrackingProtectionEnabled =
        "privacy.trackingprotection.emailtracking.enabled";
      this.prefEmailTrackingProtectionEnabledInPrivateWindows =
        "privacy.trackingprotection.emailtracking.pbmode.enabled";

      this.enabledGlobally = false;
      this.emailTrackingProtectionEnabledGlobally = false;

      this.enabledInPrivateWindows = false;
      this.emailTrackingProtectionEnabledInPrivateWindows = false;

      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "trackingTable",
        this.prefTrackingTable,
        ""
      );
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "trackingAnnotationTable",
        this.prefTrackingAnnotationTable,
        ""
      );
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "annotationsLevel2Enabled",
        this.prefAnnotationsLevel2Enabled,
        false
      );
    }

    init() {
      this.updateEnabled();

      Services.prefs.addObserver(this.prefEnabled, this);
      Services.prefs.addObserver(this.prefEnabledInPrivateWindows, this);
      Services.prefs.addObserver(this.prefEmailTrackingProtectionEnabled, this);
      Services.prefs.addObserver(
        this.prefEmailTrackingProtectionEnabledInPrivateWindows,
        this
      );
    }

    uninit() {
      Services.prefs.removeObserver(this.prefEnabled, this);
      Services.prefs.removeObserver(this.prefEnabledInPrivateWindows, this);
      Services.prefs.removeObserver(
        this.prefEmailTrackingProtectionEnabled,
        this
      );
      Services.prefs.removeObserver(
        this.prefEmailTrackingProtectionEnabledInPrivateWindows,
        this
      );
    }

    observe() {
      this.updateEnabled();
      this.updateCategoryItem();
    }

    get trackingProtectionLevel2Enabled() {
      const CONTENT_TABLE = "content-track-digest256";
      return this.trackingTable.includes(CONTENT_TABLE);
    }

    get enabled() {
      return (
        this.enabledGlobally ||
        this.emailTrackingProtectionEnabledGlobally ||
        (this.isWindowPrivate &&
          (this.enabledInPrivateWindows ||
            this.emailTrackingProtectionEnabledInPrivateWindows))
      );
    }

    updateEnabled() {
      this.enabledGlobally = Services.prefs.getBoolPref(this.prefEnabled);
      this.enabledInPrivateWindows = Services.prefs.getBoolPref(
        this.prefEnabledInPrivateWindows
      );
      this.emailTrackingProtectionEnabledGlobally = Services.prefs.getBoolPref(
        this.prefEmailTrackingProtectionEnabled
      );
      this.emailTrackingProtectionEnabledInPrivateWindows =
        Services.prefs.getBoolPref(
          this.prefEmailTrackingProtectionEnabledInPrivateWindows
        );
    }

    isAllowingLevel1(state) {
      return (
        (state &
          Ci.nsIWebProgressListener.STATE_LOADED_LEVEL_1_TRACKING_CONTENT) !=
        0
      );
    }

    isAllowingLevel2(state) {
      return (
        (state &
          Ci.nsIWebProgressListener.STATE_LOADED_LEVEL_2_TRACKING_CONTENT) !=
        0
      );
    }

    isAllowing(state) {
      return this.isAllowingLevel1(state) || this.isAllowingLevel2(state);
    }

    async updateSubView() {
      let previousURI = gBrowser.currentURI.spec;
      let previousWindow = gBrowser.selectedBrowser.innerWindowID;

      let { items, anyShimAllowed } = await this._generateSubViewListItems();

      // If we don't have trackers we would usually not show the menu item
      // allowing the user to show the sub-panel. However, in the edge case
      // that we annotated trackers on the page using the strict list but did
      // not detect trackers on the page using the basic list, we currently
      // still show the panel. To reduce the confusion, tell the user that we have
      // not detected any tracker.
      if (!items.childNodes.length) {
        let emptyImage = document.createXULElement("image");
        emptyImage.classList.add("protections-popup-trackersView-empty-image");
        emptyImage.classList.add("trackers-icon");

        let emptyLabel = document.createXULElement("label");
        emptyLabel.classList.add("protections-popup-empty-label");
        document.l10n.setAttributes(
          emptyLabel,
          "content-blocking-trackers-view-empty"
        );

        items.appendChild(emptyImage);
        items.appendChild(emptyLabel);

        this.subViewList.classList.add("empty");
      } else {
        this.subViewList.classList.remove("empty");
      }

      // This might have taken a while. Only update the list if we're still on the same page.
      if (
        previousURI == gBrowser.currentURI.spec &&
        previousWindow == gBrowser.selectedBrowser.innerWindowID
      ) {
        this.subViewShimAllowHint.hidden = !anyShimAllowed;

        this.subViewList.textContent = "";
        this.subViewList.append(items);
        const l10nId =
          this.enabled && !gProtectionsHandler.hasException
            ? "protections-blocking-tracking-content"
            : "protections-not-blocking-tracking-content";
        document.l10n.setAttributes(this.subView, l10nId);
      }
    }

    async _createListItem(origin, actions) {
      // Figure out if this list entry was actually detected by TP or something else.
      let isAllowed = actions.some(
        ([state]) => this.isAllowing(state) && !this.isShimming(state)
      );
      let isDetected =
        isAllowed || actions.some(([state]) => this.isBlocking(state));

      if (!isDetected) {
        return {};
      }

      // Because we might use different lists for annotation vs. blocking, we
      // need to make sure that this is a tracker that we would actually have blocked
      // before showing it to the user.
      if (
        this.annotationsLevel2Enabled &&
        !this.trackingProtectionLevel2Enabled &&
        actions.some(
          ([state]) =>
            (state &
              Ci.nsIWebProgressListener
                .STATE_LOADED_LEVEL_2_TRACKING_CONTENT) !=
            0
        )
      ) {
        return {};
      }

      let listItem = document.createElementNS(
        "http://www.w3.org/1999/xhtml",
        "div"
      );
      listItem.className = "protections-popup-list-item";
      listItem.classList.toggle("allowed", isAllowed);

      let label = document.createXULElement("label");
      // Repeat the host in the tooltip in case it's too long
      // and overflows in our panel.
      label.tooltipText = origin;
      label.value = origin;
      label.className = "protections-popup-list-host-label";
      label.setAttribute("crop""end");
      listItem.append(label);

      let shimAllowed = actions.some(([flag]) => flag == this._flags.allow);
      if (shimAllowed) {
        listItem.append(this._getShimAllowIndicator());
      }

      return { item: listItem, shimAllowed };
    }
  })();

let ThirdPartyCookies =
  new (class ThirdPartyCookies extends ProtectionCategory {
    constructor() {
      super(
        "cookies",
        {
          // This would normally expect a boolean pref. However, this category
          // overwrites the enabled getter for custom handling of cookie behavior
          // states.
          prefEnabled: "network.cookie.cookieBehavior",
        },
        {
          // ThirdPartyCookies implements custom flag processing.
          allow: null,
          shim: null,
          load: null,
          block: null,
        }
      );

      ChromeUtils.defineLazyGetter(this"categoryLabel", () =>
        document.getElementById("protections-popup-cookies-category-label")
      );

      this.prefEnabledValues = [
        // These values match the ones exposed under the Content Blocking section
        // of the Preferences UI.
        Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN, // Block all third-party cookies
        Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER, // Block third-party cookies from trackers
        Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, // Block trackers and patition third-party trackers
        Ci.nsICookieService.BEHAVIOR_REJECT, // Block all cookies
      ];

      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "behaviorPref",
        this.prefEnabled,
        Ci.nsICookieService.BEHAVIOR_ACCEPT,
        this.updateCategoryItem.bind(this)
      );
    }

    isBlocking(state) {
      return (
        (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_TRACKER) !=
          0 ||
        (state &
          Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_SOCIALTRACKER) !=
          0 ||
        (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_ALL) != 0 ||
        (state &
          Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_BY_PERMISSION) !=
          0 ||
        (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_FOREIGN) !=
          0 ||
        (state & Ci.nsIWebProgressListener.STATE_COOKIES_PARTITIONED_TRACKER) !=
          0
      );
    }

    isDetected(state) {
      if (this.isBlocking(state)) {
        return true;
      }

      if (
        [
          Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
          Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER,
          Ci.nsICookieService.BEHAVIOR_ACCEPT,
        ].includes(this.behaviorPref)
      ) {
        return (
          (state & Ci.nsIWebProgressListener.STATE_COOKIES_LOADED_TRACKER) !=
            0 ||
          (SocialTracking.enabled &&
            (state &
              Ci.nsIWebProgressListener.STATE_COOKIES_LOADED_SOCIALTRACKER) !=
              0)
        );
      }

      // We don't have specific flags for the other cookie behaviors so just
      // fall back to STATE_COOKIES_LOADED.
      return (state & Ci.nsIWebProgressListener.STATE_COOKIES_LOADED) != 0;
    }

    updateCategoryItem() {
      if (!super.updateCategoryItem()) {
        return;
      }

      let l10nId;
      if (!this.enabled) {
        l10nId = "content-blocking-cookies-blocking-trackers-label";
      } else {
        switch (this.behaviorPref) {
          case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
            l10nId = "content-blocking-cookies-blocking-third-party-label";
            break;
          case Ci.nsICookieService.BEHAVIOR_REJECT:
            l10nId = "content-blocking-cookies-blocking-all-label";
            break;
          case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
            l10nId = "content-blocking-cookies-blocking-unvisited-label";
            break;
          case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
          case Ci.nsICookieService
            .BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN:
            l10nId = "content-blocking-cookies-blocking-trackers-label";
            break;
          default:
            console.error(
              `Error: Unknown cookieBehavior pref observed: ${this.behaviorPref}`
            );
            this.categoryLabel.removeAttribute("data-l10n-id");
            this.categoryLabel.textContent = "";
            return;
        }
      }
      document.l10n.setAttributes(this.categoryLabel, l10nId);
    }

    get enabled() {
      return this.prefEnabledValues.includes(this.behaviorPref);
    }

    updateSubView() {
      let contentBlockingLog = gBrowser.selectedBrowser.getContentBlockingLog();
      contentBlockingLog = JSON.parse(contentBlockingLog);

      let categories = this._processContentBlockingLog(contentBlockingLog);

      this.subViewList.textContent = "";

      let categoryNames = ["trackers"];
      switch (this.behaviorPref) {
        case Ci.nsICookieService.BEHAVIOR_REJECT:
          categoryNames.push("firstParty");
        // eslint-disable-next-line no-fallthrough
        case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
          categoryNames.push("thirdParty");
      }

      for (let category of categoryNames) {
        let itemsToShow = categories[category];

        if (!itemsToShow.length) {
          continue;
        }

        let box = document.createXULElement("vbox");
        box.className = "protections-popup-cookiesView-list-section";
        let label = document.createXULElement("label");
        label.className = "protections-popup-cookiesView-list-header";
        let l10nId;
        switch (category) {
          case "trackers":
            l10nId = "content-blocking-cookies-view-trackers-label";
            break;
          case "firstParty":
            l10nId = "content-blocking-cookies-view-first-party-label";
            break;
          case "thirdParty":
            l10nId = "content-blocking-cookies-view-third-party-label";
            break;
        }
        if (l10nId) {
          document.l10n.setAttributes(label, l10nId);
        }
        box.appendChild(label);

        for (let info of itemsToShow) {
          box.appendChild(this._createListItem(info));
        }

        this.subViewList.appendChild(box);
      }

      this.subViewHeading.hidden = false;
      if (!this.enabled) {
        document.l10n.setAttributes(
          this.subView,
          "protections-not-blocking-cross-site-tracking-cookies"
        );
        return;
      }

      let l10nId;
      let siteException = gProtectionsHandler.hasException;
      switch (this.behaviorPref) {
        case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
          l10nId = siteException
            ? "protections-not-blocking-cookies-third-party"
            : "protections-blocking-cookies-third-party";
          this.subViewHeading.hidden = true;
          if (this.subViewHeading.nextSibling.nodeName == "toolbarseparator") {
            this.subViewHeading.nextSibling.hidden = true;
          }
          break;
        case Ci.nsICookieService.BEHAVIOR_REJECT:
          l10nId = siteException
            ? "protections-not-blocking-cookies-all"
            : "protections-blocking-cookies-all";
          this.subViewHeading.hidden = true;
          if (this.subViewHeading.nextSibling.nodeName == "toolbarseparator") {
            this.subViewHeading.nextSibling.hidden = true;
          }
          break;
        case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
          l10nId = "protections-blocking-cookies-unvisited";
          this.subViewHeading.hidden = true;
          if (this.subViewHeading.nextSibling.nodeName == "toolbarseparator") {
            this.subViewHeading.nextSibling.hidden = true;
          }
          break;
        case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
        case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN:
          l10nId = siteException
            ? "protections-not-blocking-cross-site-tracking-cookies"
            : "protections-blocking-cookies-trackers";
          break;
        default:
          console.error(
            `Error: Unknown cookieBehavior pref when updating subview: ${this.behaviorPref}`
          );
          return;
      }

      document.l10n.setAttributes(this.subView, l10nId);
    }

    _getExceptionState(origin) {
      let thirdPartyStorage = Services.perms.testPermissionFromPrincipal(
        gBrowser.contentPrincipal,
        "3rdPartyStorage^" + origin
      );

      if (thirdPartyStorage != Services.perms.UNKNOWN_ACTION) {
        return thirdPartyStorage;
      }

      let principal =
        Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin);
      // Cookie exceptions get "inherited" from parent- to sub-domain, so we need to
      // make sure to include parent domains in the permission check for "cookie".
      return Services.perms.testPermissionFromPrincipal(principal, "cookie");
    }

    _clearException(origin) {
      for (let perm of Services.perms.getAllForPrincipal(
        gBrowser.contentPrincipal
      )) {
        if (perm.type == "3rdPartyStorage^" + origin) {
          Services.perms.removePermission(perm);
        }
      }

      // OAs don't matter here, so we can just use the hostname.
      let host = Services.io.newURI(origin).host;

      // Cookie exceptions get "inherited" from parent- to sub-domain, so we need to
      // clear any cookie permissions from parent domains as well.
      for (let perm of Services.perms.all) {
        if (
          perm.type == "cookie" &&
          Services.eTLD.hasRootDomain(host, perm.principal.host)
        ) {
          Services.perms.removePermission(perm);
        }
      }
    }

    // Transforms and filters cookie entries in the content blocking log
    // so that we can categorize and display them in the UI.
    _processContentBlockingLog(log) {
      let newLog = {
        firstParty: [],
        trackers: [],
        thirdParty: [],
      };

      let firstPartyDomain = null;
      try {
        firstPartyDomain = Services.eTLD.getBaseDomain(gBrowser.currentURI);
      } catch (e) {
        // There are nasty edge cases here where someone is trying to set a cookie
        // on a public suffix or an IP address. Just categorize those as third party...
        if (
          e.result != Cr.NS_ERROR_HOST_IS_IP_ADDRESS &&
          e.result != Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
        ) {
          throw e;
        }
      }

      for (let [origin, actions] of Object.entries(log)) {
        if (!origin.startsWith("http")) {
          continue;
        }

        let info = {
          origin,
          isAllowed: true,
          exceptionState: this._getExceptionState(origin),
        };
        let hasCookie = false;
        let isTracker = false;

        // Extract information from the states entries in the content blocking log.
        // Each state will contain a single state flag from nsIWebProgressListener.
        // Note that we are using the same helper functions that are applied to the
        // bit map passed to onSecurityChange (which contains multiple states), thus
        // not checking exact equality, just presence of bits.
        for (let [state, blocked] of actions) {
          if (this.isDetected(state)) {
            hasCookie = true;
          }
          if (TrackingProtection.isAllowing(state)) {
            isTracker = true;
          }
          // blocked tells us whether the resource was actually blocked
          // (which it may not be in case of an exception).
          if (this.isBlocking(state)) {
            info.isAllowed = !blocked;
          }
        }

        if (!hasCookie) {
          continue;
        }

        let isFirstParty = false;
        try {
          let uri = Services.io.newURI(origin);
          isFirstParty = Services.eTLD.getBaseDomain(uri) == firstPartyDomain;
        } catch (e) {
          if (
            e.result != Cr.NS_ERROR_HOST_IS_IP_ADDRESS &&
            e.result != Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
          ) {
            throw e;
          }
        }

        if (isFirstParty) {
          newLog.firstParty.push(info);
        } else if (isTracker) {
          newLog.trackers.push(info);
        } else {
          newLog.thirdParty.push(info);
        }
      }

      return newLog;
    }

    _createListItem({ origin, isAllowed, exceptionState }) {
      let listItem = document.createElementNS(
        "http://www.w3.org/1999/xhtml",
        "div"
      );
      listItem.className = "protections-popup-list-item";
      // Repeat the origin in the tooltip in case it's too long
      // and overflows in our panel.
      listItem.tooltipText = origin;

      let label = document.createXULElement("label");
      label.value = origin;
      label.className = "protections-popup-list-host-label";
      label.setAttribute("crop""end");
      listItem.append(label);

      if (
        (isAllowed && exceptionState == Services.perms.ALLOW_ACTION) ||
        (!isAllowed && exceptionState == Services.perms.DENY_ACTION)
      ) {
        listItem.classList.add("protections-popup-list-item-with-state");

        let stateLabel = document.createXULElement("label");
        stateLabel.className = "protections-popup-list-state-label";
        let l10nId;
        if (isAllowed) {
          l10nId = "content-blocking-cookies-view-allowed-label";
          listItem.classList.toggle("allowed"true);
        } else {
          l10nId = "content-blocking-cookies-view-blocked-label";
        }
        document.l10n.setAttributes(stateLabel, l10nId);

        let removeException = document.createXULElement("button");
        removeException.className = "permission-popup-permission-remove-button";
        document.l10n.setAttributes(
          removeException,
          "content-blocking-cookies-view-remove-button",
          { domain: origin }
        );
        removeException.appendChild(stateLabel);

        removeException.addEventListener(
          "click",
          () => {
            this._clearException(origin);
            removeException.remove();
            listItem.classList.toggle("allowed", !isAllowed);
          },
          { once: true }
        );
        listItem.append(removeException);
      }

      return listItem;
    }
  })();

let SocialTracking =
  new (class SocialTrackingProtection extends ProtectionCategory {
    constructor() {
      super(
        "socialblock",
        {
          prefEnabled: "privacy.socialtracking.block_cookies.enabled",
        },
        {
          load: Ci.nsIWebProgressListener.STATE_LOADED_SOCIALTRACKING_CONTENT,
          block: Ci.nsIWebProgressListener.STATE_BLOCKED_SOCIALTRACKING_CONTENT,
        }
      );

      this.prefStpTpEnabled =
        "privacy.trackingprotection.socialtracking.enabled";
      this.prefSTPCookieEnabled = this.prefEnabled;
      this.prefCookieBehavior = "network.cookie.cookieBehavior";

      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "socialTrackingProtectionEnabled",
        this.prefStpTpEnabled,
        false,
        this.updateCategoryItem.bind(this)
      );
      XPCOMUtils.defineLazyPreferenceGetter(
        this,
        "rejectTrackingCookies",
        this.prefCookieBehavior,
        null,
        this.updateCategoryItem.bind(this),
        val =>
          [
            Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER,
            Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
          ].includes(val)
      );
    }

    get blockingEnabled() {
      return (
        (this.socialTrackingProtectionEnabled || this.rejectTrackingCookies) &&
        this.enabled
      );
    }

    isBlockingCookies(state) {
      return (
        (state &
          Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_SOCIALTRACKER) !=
        0
      );
    }

    isBlocking(state) {
      return super.isBlocking(state) || this.isBlockingCookies(state);
    }

    isAllowing(state) {
      if (this.socialTrackingProtectionEnabled) {
        return super.isAllowing(state);
      }

      return (
        (state &
          Ci.nsIWebProgressListener.STATE_COOKIES_LOADED_SOCIALTRACKER) !=
        0
      );
    }

    updateCategoryItem() {
      // Can't get `this.categoryItem` without the popup. Using the popup instead
      // of `this.categoryItem` to guard access, because the category item getter
      // can trigger bug 1543537. If there's no popup, we'll be called again the
      // first time the popup shows.
      if (!gProtectionsHandler._protectionsPopup) {
        return;
      }
      if (this.enabled) {
        this.categoryItem.removeAttribute("uidisabled");
      } else {
        this.categoryItem.setAttribute("uidisabled"true);
      }
      this.categoryItem.classList.toggle("blocked"this.blockingEnabled);
    }
  })();

/**
 * Singleton to manage the cookie banner feature section in the protections
 * panel and the cookie banner handling subview.
 */

let cookieBannerHandling = new (class {
  // Check if this is a private window. We don't expect PBM state to change
  // during the lifetime of this window.
  #isPrivateBrowsing = PrivateBrowsingUtils.isWindowPrivate(window);

  constructor() {
    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "_serviceModePref",
      "cookiebanners.service.mode",
      Ci.nsICookieBannerService.MODE_DISABLED
    );
    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "_serviceModePrefPrivateBrowsing",
      "cookiebanners.service.mode.privateBrowsing",
      Ci.nsICookieBannerService.MODE_DISABLED
    );
    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "_serviceDetectOnly",
      "cookiebanners.service.detectOnly",
      false
    );
    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "_uiEnabled",
      "cookiebanners.ui.desktop.enabled",
      false
    );
    ChromeUtils.defineLazyGetter(this"_cookieBannerSection", () =>
      document.getElementById("protections-popup-cookie-banner-section")
    );
    ChromeUtils.defineLazyGetter(this"_cookieBannerSectionSeparator", () =>
      document.getElementById(
        "protections-popup-cookie-banner-section-separator"
      )
    );
    ChromeUtils.defineLazyGetter(this"_cookieBannerSwitch", () =>
      document.getElementById("protections-popup-cookie-banner-switch")
    );
    ChromeUtils.defineLazyGetter(this"_cookieBannerSubview", () =>
      document.getElementById("protections-popup-cookieBannerView")
    );
    ChromeUtils.defineLazyGetter(this"_cookieBannerEnableSite", () =>
      document.getElementById("cookieBannerView-enable-site")
    );
    ChromeUtils.defineLazyGetter(this"_cookieBannerDisableSite", () =>
      document.getElementById("cookieBannerView-disable-site")
    );
  }

  /**
   * Tests if the current site has a user-created exception from the default
   * cookie banner handling mode. Currently that means the feature is disabled
   * for the current site.
   *
   * Note: bug 1790688 will move this mode handling logic into the
   * nsCookieBannerService.
   *
   * @returns {boolean} - true if the user has manually created an exception.
   */

  get #hasException() {
    // If the CBH feature is preffed off, we can't have an exception.
    if (!Services.cookieBanners.isEnabled) {
      return false;
    }

    // URLs containing IP addresses are not supported by the CBH service, and
    // will throw. In this case, users can't create an exception, so initialize
    // `pref` to the default value returned by `getDomainPref`.
    let pref = Ci.nsICookieBannerService.MODE_UNSET;
    try {
      pref = Services.cookieBanners.getDomainPref(
        gBrowser.currentURI,
        this.#isPrivateBrowsing
      );
    } catch (ex) {
      console.error(
        "Cookie Banner Handling error checking for per-site exceptions: ",
        ex
      );
    }
    return pref == Ci.nsICookieBannerService.MODE_DISABLED;
  }

  /**
   * Tests if the cookie banner handling code supports the current site.
   *
   * See nsICookieBannerService.hasRuleForBrowsingContextTree for details.
   *
   * @returns {boolean} - true if the base domain is in the list of rules.
   */

  get isSiteSupported() {
    return (
      Services.cookieBanners.isEnabled &&
      Services.cookieBanners.hasRuleForBrowsingContextTree(
        gBrowser.selectedBrowser.browsingContext
      )
    );
  }

  /*
   * @returns {string} - Base domain (eTLD + 1) used for clearing site data.
   */

  get #currentBaseDomain() {
    return gBrowser.contentPrincipal.baseDomain;
  }

  /**
   * Helper method used by both updateSection and updateSubView to map internal
   * state to UI attribute state. We have to separately set the subview's state
   * because the subview is not a descendant of the menu item in the DOM, and
   * we rely on CSS to toggle UI visibility based on attribute state.
   *
   * @returns A string value to be set as a UI attribute value.
   */

  get #uiState() {
    if (this.#hasException) {
      return "site-disabled";
    } else if (this.isSiteSupported) {
      return "detected";
    }
    return "undetected";
  }

  updateSection() {
    let showSection = this.#shouldShowSection();
    let state = this.#uiState;

    for (let el of [
      this._cookieBannerSection,
      this._cookieBannerSectionSeparator,
    ]) {
      el.hidden = !showSection;
    }

    this._cookieBannerSection.dataset.state = state;

    // On unsupported sites, disable button styling and click behavior.
    // Note: to be replaced with a "please support site" subview in bug 1801971.
    if (state == "undetected") {
      this._cookieBannerSection.setAttribute("disabled"true);
      this._cookieBannerSwitch.classList.remove("subviewbutton-nav");
      this._cookieBannerSwitch.setAttribute("disabled"true);
    } else {
      this._cookieBannerSection.removeAttribute("disabled");
      this._cookieBannerSwitch.classList.add("subviewbutton-nav");
      this._cookieBannerSwitch.removeAttribute("disabled");
    }
  }

  #shouldShowSection() {
    // Don't show UI if globally disabled by pref, or if the cookie service
    // is in detect-only mode.
    if (!this._uiEnabled || this._serviceDetectOnly) {
      return false;
    }

    // Show the section if the feature is not in disabled mode, being sure to
    // check the different prefs for regular and private windows.
    if (this.#isPrivateBrowsing) {
      return (
        this._serviceModePrefPrivateBrowsing !=
        Ci.nsICookieBannerService.MODE_DISABLED
      );
    }
    return this._serviceModePref != Ci.nsICookieBannerService.MODE_DISABLED;
  }

  /*
   * Updates the cookie banner handling subview just before it's shown.
   */

  updateSubView() {
    this._cookieBannerSubview.dataset.state = this.#uiState;

    let baseDomain = JSON.stringify({ host: this.#currentBaseDomain });
    this._cookieBannerEnableSite.setAttribute("data-l10n-args", baseDomain);
    this._cookieBannerDisableSite.setAttribute("data-l10n-args", baseDomain);
  }

  async #disableCookieBannerHandling() {
    // We can't clear data during a private browsing session until bug 1818783
    // is fixed. In the meantime, don't allow the cookie banner controls in a
    // private window to clear data for regular browsing mode.
    if (!this.#isPrivateBrowsing) {
      await SiteDataManager.remove(this.#currentBaseDomain);
    }
    Services.cookieBanners.setDomainPref(
      gBrowser.currentURI,
      Ci.nsICookieBannerService.MODE_DISABLED,
      this.#isPrivateBrowsing
    );
  }

  #enableCookieBannerHandling() {
    Services.cookieBanners.removeDomainPref(
      gBrowser.currentURI,
      this.#isPrivateBrowsing
    );
  }

  async onCookieBannerToggleCommand() {
    let hasException =
      this._cookieBannerSection.toggleAttribute("hasException");
    if (hasException) {
      await this.#disableCookieBannerHandling();
      Glean.securityUiProtectionspopup.clickCookiebToggleOff.record();
    } else {
      this.#enableCookieBannerHandling();
      Glean.securityUiProtectionspopup.clickCookiebToggleOn.record();
    }
    gProtectionsHandler._hidePopup();
    gBrowser.reloadTab(gBrowser.selectedTab);
  }
})();

/**
 * Utility object to handle manipulations of the protections indicators in the UI
 */

var gProtectionsHandler = {
  PREF_CB_CATEGORY: "browser.contentblocking.category",

  /**
   * Contains an array of smartblock compatible sites and information on the corresponding shim
   * sites is a list of compatible sites
   * shimId is the id of the shim blocking content from the origin
   * displayName is the name shown for the toggle used for blocking/unblocking the origin
   */

  smartblockEmbedInfo: [
    {
      sites: ["https://itisatracker.org"],
      shimId: "EmbedTestShim",
      displayName: "Test",
    },
    {
      sites: ["https://www.instagram.com", "https://platform.instagram.com"],
      shimId: "InstagramEmbed",
      displayName: "Instagram",
    },
    {
      sites: ["https://www.tiktok.com"],
      shimId: "TiktokEmbed",
      displayName: "Tiktok",
    },
  ],

  /**
   * Keeps track of if a smartblock toggle has been clicked since the panel was opened. Resets
   * everytime the panel is closed. Used for telemetry purposes.
   */

  _hasClickedSmartBlockEmbedToggle: false,

  /**
   * Keeps track of what was responsible for opening the protections panel popup. Used for
   * telemetry purposes.
   */

  _protectionsPopupOpeningReason: null,

  _protectionsPopup: null,
  _initializePopup() {
    if (!this._protectionsPopup) {
      let wrapper = document.getElementById("template-protections-popup");
      this._protectionsPopup = wrapper.content.firstElementChild;
      this._protectionsPopup.addEventListener("popupshown"this);
      this._protectionsPopup.addEventListener("popuphidden"this);
      wrapper.replaceWith(wrapper.content);

      this.maybeSetMilestoneCounterText();

      for (let blocker of Object.values(this.blockers)) {
        blocker.updateCategoryItem();
      }

      this._protectionsPopup.addEventListener("command"this);
      this._protectionsPopup.addEventListener("popupshown"this);
      this._protectionsPopup.addEventListener("popuphidden"this);

      function openTooltip(event) {
        document.getElementById(event.target.tooltip).openPopup(event.target);
      }
      function closeTooltip(event) {
        document.getElementById(event.target.tooltip).hidePopup();
      }
      let notBlockingWhy = document.getElementById(
        "protections-popup-not-blocking-section-why"
      );
      notBlockingWhy.addEventListener("mouseover", openTooltip);
      notBlockingWhy.addEventListener("focus", openTooltip);
      notBlockingWhy.addEventListener("mouseout", closeTooltip);
      notBlockingWhy.addEventListener("blur", closeTooltip);

      document
        .getElementById(
          "protections-popup-trackers-blocked-counter-description"
        )
        .addEventListener("click", () =>
          gProtectionsHandler.openProtections(true)
        );
      document
        .getElementById("protections-popup-cookie-banner-switch")
        .addEventListener("click", () =>
          gProtectionsHandler.onCookieBannerClick()
        );
    }
  },

  _hidePopup() {
    if (this._protectionsPopup) {
      PanelMultiView.hidePopup(this._protectionsPopup);
    }
  },

  // smart getters
  get iconBox() {
    delete this.iconBox;
    return (this.iconBox = document.getElementById(
      "tracking-protection-icon-box"
    ));
  },
  get _protectionsPopupMultiView() {
    delete this._protectionsPopupMultiView;
    return (this._protectionsPopupMultiView = document.getElementById(
      "protections-popup-multiView"
    ));
  },
  get _protectionsPopupMainView() {
    delete this._protectionsPopupMainView;
    return (this._protectionsPopupMainView = document.getElementById(
      "protections-popup-mainView"
    ));
  },
  get _protectionsPopupMainViewHeaderLabel() {
    delete this._protectionsPopupMainViewHeaderLabel;
    return (this._protectionsPopupMainViewHeaderLabel = document.getElementById(
      "protections-popup-mainView-panel-header-span"
    ));
  },
  get _protectionsPopupTPSwitch() {
    delete this._protectionsPopupTPSwitch;
    return (this._protectionsPopupTPSwitch = document.getElementById(
      "protections-popup-tp-switch"
    ));
  },
  get _protectionsPopupCategoryList() {
    delete this._protectionsPopupCategoryList;
    return (this._protectionsPopupCategoryList = document.getElementById(
      "protections-popup-category-list"
    ));
  },
  get _protectionsPopupBlockingHeader() {
    delete this._protectionsPopupBlockingHeader;
    return (this._protectionsPopupBlockingHeader = document.getElementById(
      "protections-popup-blocking-section-header"
    ));
  },
  get _protectionsPopupNotBlockingHeader() {
    delete this._protectionsPopupNotBlockingHeader;
    return (this._protectionsPopupNotBlockingHeader = document.getElementById(
      "protections-popup-not-blocking-section-header"
    ));
  },
  get _protectionsPopupNotFoundHeader() {
    delete this._protectionsPopupNotFoundHeader;
    return (this._protectionsPopupNotFoundHeader = document.getElementById(
      "protections-popup-not-found-section-header"
    ));
  },
  get _protectionsPopupSmartblockContainer() {
    delete this._protectionsPopupSmartblockContainer;
    return (this._protectionsPopupSmartblockContainer = document.getElementById(
      "protections-popup-smartblock-highlight-container"
    ));
  },
  get _protectionsPopupSmartblockDescription() {
    delete this._protectionsPopupSmartblockDescription;
    return (this._protectionsPopupSmartblockDescription =
      document.getElementById("protections-popup-smartblock-description"));
  },
  get _protectionsPopupSmartblockToggleContainer() {
    delete this._protectionsPopupSmartblockToggleContainer;
    return (this._protectionsPopupSmartblockToggleContainer =
      document.getElementById("protections-popup-smartblock-toggle-container"));
  },
  get _protectionsPopupSettingsButton() {
    delete this._protectionsPopupSettingsButton;
    return (this._protectionsPopupSettingsButton = document.getElementById(
      "protections-popup-settings-button"
    ));
  },
  get _protectionsPopupFooter() {
    delete this._protectionsPopupFooter;
    return (this._protectionsPopupFooter = document.getElementById(
      "protections-popup-footer"
    ));
  },
  get _protectionsPopupTrackersCounterBox() {
    delete this._protectionsPopupTrackersCounterBox;
    return (this._protectionsPopupTrackersCounterBox = document.getElementById(
      "protections-popup-trackers-blocked-counter-box"
    ));
  },
  get _protectionsPopupTrackersCounterDescription() {
    delete this._protectionsPopupTrackersCounterDescription;
    return (this._protectionsPopupTrackersCounterDescription =
      document.getElementById(
        "protections-popup-trackers-blocked-counter-description"
      ));
  },
  get _protectionsPopupFooterProtectionTypeLabel() {
    delete this._protectionsPopupFooterProtectionTypeLabel;
    return (this._protectionsPopupFooterProtectionTypeLabel =
      document.getElementById(
        "protections-popup-footer-protection-type-label"
      ));
  },
  get _trackingProtectionIconTooltipLabel() {
    delete this._trackingProtectionIconTooltipLabel;
    return (this._trackingProtectionIconTooltipLabel = document.getElementById(
      "tracking-protection-icon-tooltip-label"
    ));
  },
  get _trackingProtectionIconContainer() {
    delete this._trackingProtectionIconContainer;
    return (this._trackingProtectionIconContainer = document.getElementById(
      "tracking-protection-icon-container"
    ));
  },

  get noTrackersDetectedDescription() {
    delete this.noTrackersDetectedDescription;
    return (this.noTrackersDetectedDescription = document.getElementById(
      "protections-popup-no-trackers-found-description"
    ));
  },

  get _protectionsPopupMilestonesText() {
    delete this._protectionsPopupMilestonesText;
    return (this._protectionsPopupMilestonesText = document.getElementById(
      "protections-popup-milestones-text"
    ));
  },

  get _notBlockingWhyLink() {
    delete this._notBlockingWhyLink;
    return (this._notBlockingWhyLink = document.getElementById(
      "protections-popup-not-blocking-section-why"
    ));
  },

  // A list of blockers that will be displayed in the categories list
  // when blockable content is detected. A blocker must be an object
  // with at least the following two properties:
  //  - enabled: Whether the blocker is currently turned on.
  //  - isDetected(state): Given a content blocking state, whether the blocker has
  //                       either allowed or blocked elements.
  //  - categoryItem: The DOM item that represents the entry in the category list.
  //
  // It may also contain an init() and uninit() function, which will be called
  // on gProtectionsHandler.init() and gProtectionsHandler.uninit().
  // The buttons in the protections panel will appear in the same order as this array.
  blockers: {
    SocialTracking,
    ThirdPartyCookies,
    TrackingProtection,
    Fingerprinting,
    Cryptomining,
  },

  init() {
    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "_fontVisibilityTrackingProtection",
      "layout.css.font-visibility.trackingprotection",
      3000
    );

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "_protectionsPopupToastTimeout",
      "browser.protections_panel.toast.timeout",
      3000
    );

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "_protectionsPopupButtonDelay",
      "security.notification_enable_delay",
      500
    );

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "milestoneListPref",
      "browser.contentblocking.cfr-milestone.milestones",
      "[]",
      () => this.maybeSetMilestoneCounterText(),
      val => JSON.parse(val)
    );

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "milestonePref",
      "browser.contentblocking.cfr-milestone.milestone-achieved",
      0,
      () => this.maybeSetMilestoneCounterText()
    );

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "milestoneTimestampPref",
      "browser.contentblocking.cfr-milestone.milestone-shown-time",
      "0",
      null,
      val => parseInt(val)
    );

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "milestonesEnabledPref",
      "browser.contentblocking.cfr-milestone.enabled",
      false,
      () => this.maybeSetMilestoneCounterText()
    );

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "protectionsPanelMessageSeen",
      "browser.protections_panel.infoMessage.seen",
      false
    );

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "smartblockEmbedsEnabledPref",
      "extensions.webcompat.smartblockEmbeds.enabled",
      false
    );

    for (let blocker of Object.values(this.blockers)) {
      if (blocker.init) {
        blocker.init();
      }
    }

    // Add an observer to observe that the history has been cleared.
    Services.obs.addObserver(this"browser:purge-session-history");
    // Add an observer to listen to requests to open the protections panel
    Services.obs.addObserver(this"smartblock:open-protections-panel");

    // bind the reset toggle sec delay function to this so we can use it
    // as an event listener without this becoming the event target inside
    // the function
    this._resetToggleSecDelay = this._resetToggleSecDelay.bind(this);
  },

  uninit() {
    for (let blocker of Object.values(this.blockers)) {
      if (blocker.uninit) {
        blocker.uninit();
      }
    }

    Services.obs.removeObserver(this"browser:purge-session-history");
    Services.obs.removeObserver(this"smartblock:open-protections-panel");
  },

  getTrackingProtectionLabel() {
    const value = Services.prefs.getStringPref(this.PREF_CB_CATEGORY);

    switch (value) {
      case "strict":
        return "protections-popup-footer-protection-label-strict";
      case "custom":
        return "protections-popup-footer-protection-label-custom";
      case "standard":
      /* fall through */
      default:
        return "protections-popup-footer-protection-label-standard";
    }
  },

  openPreferences(origin) {
    openPreferences("privacy-trackingprotection", { origin });
  },

  openProtections(relatedToCurrent = false) {
    switchToTabHavingURI("about:protections"true, {
      replaceQueryString: true,
      relatedToCurrent,
      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
    });

    // Don't show the milestones section anymore.
    Services.prefs.clearUserPref(
      "browser.contentblocking.cfr-milestone.milestone-shown-time"
    );
  },

  async showTrackersSubview() {
    await TrackingProtection.updateSubView();
    this._protectionsPopupMultiView.showSubView(
      "protections-popup-trackersView"
    );
  },

  async showSocialblockerSubview() {
    await SocialTracking.updateSubView();
    this._protectionsPopupMultiView.showSubView(
      "protections-popup-socialblockView"
    );
  },

  async showCookiesSubview() {
    await ThirdPartyCookies.updateSubView();
    this._protectionsPopupMultiView.showSubView(
      "protections-popup-cookiesView"
    );
  },

  async showFingerprintersSubview() {
    await Fingerprinting.updateSubView();
    this._protectionsPopupMultiView.showSubView(
      "protections-popup-fingerprintersView"
    );
  },

  async showCryptominersSubview() {
    await Cryptomining.updateSubView();
    this._protectionsPopupMultiView.showSubView(
      "protections-popup-cryptominersView"
    );
  },

  async onCookieBannerClick() {
    if (!cookieBannerHandling.isSiteSupported) {
      return;
    }
    await cookieBannerHandling.updateSubView();
    this._protectionsPopupMultiView.showSubView(
      "protections-popup-cookieBannerView"
    );
  },

  shieldHistogramAdd(value) {
    if (PrivateBrowsingUtils.isWindowPrivate(window)) {
      return;
    }
    Glean.contentblocking.trackingProtectionShield.accumulateSingleSample(
      value
    );
  },

  cryptominersHistogramAdd(value) {
    Glean.contentblocking.cryptominersBlockedCount[value].add(1);
  },

  fingerprintersHistogramAdd(value) {
    Glean.contentblocking.fingerprintersBlockedCount[value].add(1);
  },

  handleProtectionsButtonEvent(event) {
    event.stopPropagation();
    if (
      (event.type == "click" && event.button != 0) ||
      (event.type == "keypress" &&
        event.charCode != KeyEvent.DOM_VK_SPACE &&
        event.keyCode != KeyEvent.DOM_VK_RETURN)
    ) {
      return// Left click, space or enter only
    }

    this.showProtectionsPopup({ event, openingReason: "shieldButtonClicked" });
  },

  onPopupShown(event) {
    if (event.target == this._protectionsPopup) {
      PopupNotifications.suppressWhileOpen(this._protectionsPopup);

      window.addEventListener("focus"thistrue);
      this._protectionsPopupTPSwitch.addEventListener("toggle"this);

      // Insert the info message if needed. This will be shown once and then
      // remain collapsed.
      this._insertProtectionsPanelInfoMessage(event);

      // Record telemetry for open, don't record if the panel open is only a toast
      if (!event.target.hasAttribute("toast")) {
        Glean.securityUiProtectionspopup.openProtectionsPopup.record({
          openingReason: this._protectionsPopupOpeningReason,
          smartblockEmbedTogglesShown:
            !this._protectionsPopupSmartblockContainer.hidden,
        });
      }

      // Add the "open" attribute to the tracking protection icon container
      // for styling.
      // Only set the attribute once the panel is opened to avoid icon being
      // incorrectly highlighted if opening is cancelled. See Bug 1926460.
      this._trackingProtectionIconContainer.setAttribute("open""true");

      // Disable the toggles for a short time after opening via SmartBlock placeholder button
      // to prevent clickjacking.
      if (this._protectionsPopupOpeningReason == "embedPlaceholderButton") {
        this._disablePopupToggles();
        this._protectionsPopupToggleDelayTimer = setTimeout(() => {
          this._enablePopupToggles();
          delete this._protectionsPopupToggleDelayTimer;
        }, this._protectionsPopupButtonDelay);
      }

      ReportBrokenSite.updateParentMenu(event);
    }
  },

  onPopupHidden(event) {
    if (event.target == this._protectionsPopup) {
      window.removeEventListener("focus"thistrue);
      this._protectionsPopupTPSwitch.removeEventListener("toggle"this);

      // Record close telemetry, don't record for toasts
      if (!event.target.hasAttribute("toast")) {
        Glean.securityUiProtectionspopup.closeProtectionsPopup.record({
          openingReason: this._protectionsPopupOpeningReason,
          smartblockToggleClicked: this._hasClickedSmartBlockEmbedToggle,
        });
      }

      if (this._protectionsPopupToggleDelayTimer) {
        clearTimeout(this._protectionsPopupToggleDelayTimer);
        this._enablePopupToggles();
        delete this._protectionsPopupToggleDelayTimer;
      }

      this._hasClickedSmartBlockEmbedToggle = false;
      this._protectionsPopupOpeningReason = null;
    }
  },

  async onTrackingProtectionIconHoveredOrFocused() {
    // We would try to pre-fetch the data whenever the shield icon is hovered or
    // focused. We check focus event here due to the keyboard navigation.
    if (this._updatingFooter) {
      return;
    }
    this._updatingFooter = true;

    // Take the popup out of its template.
    this._initializePopup();

    // Get the tracker count and set it to the counter in the footer.
    const trackerCount = await TrackingDBService.sumAllEvents();
    this.setTrackersBlockedCounter(trackerCount);

    // Set tracking protection label
    const l10nId = this.getTrackingProtectionLabel();
    const elem = this._protectionsPopupFooterProtectionTypeLabel;
    document.l10n.setAttributes(elem, l10nId);

    // Try to get the earliest recorded date in case that there was no record
    // during the initiation but new records come after that.
    await this.maybeUpdateEarliestRecordedDateTooltip(trackerCount);

    this._updatingFooter = false;
  },

  // This triggers from top level location changes.
  onLocationChange() {
    if (this._showToastAfterRefresh) {
      this._showToastAfterRefresh = false;

      // We only display the toast if we're still on the same page.
      if (
        this._previousURI == gBrowser.currentURI.spec &&
        this._previousOuterWindowID == gBrowser.selectedBrowser.outerWindowID
      ) {
        this.showProtectionsPopup({
          toast: true,
        });
      }
    }

    // Reset blocking and exception status so that we can send telemetry
    this.hadShieldState = false;

    // Don't deal with about:, file: etc.
    if (!ContentBlockingAllowList.canHandle(gBrowser.selectedBrowser)) {
      // We hide the icon and thus avoid showing the doorhanger, since
      // the information contained there would mostly be broken and/or
      // irrelevant anyway.
      this._trackingProtectionIconContainer.hidden = true;
      return;
    }
    this._trackingProtectionIconContainer.hidden = false;

    // Check whether the user has added an exception for this site.
    this.hasException = ContentBlockingAllowList.includes(
      gBrowser.selectedBrowser
    );

    if (this._protectionsPopup) {
      this._protectionsPopup.toggleAttribute("hasException"this.hasException);
    }
    this.iconBox.toggleAttribute("hasException"this.hasException);

    // Add to telemetry per page load as a baseline measurement.
    this.fingerprintersHistogramAdd("pageLoad");
    this.cryptominersHistogramAdd("pageLoad");
    this.shieldHistogramAdd(0);
  },

  notifyContentBlockingEvent(event) {
    // We don't notify observers until the document stops loading, therefore
    // a merged event can be sent, which gives an opportunity to decide the
    // priority by the handler.
    // Content blocking events coming after stopping will not be merged, and are
    // sent directly.
    if (!this._isStoppedState || !this.anyDetected) {
      return;
    }

    let uri = gBrowser.currentURI;
    let uriHost = uri.asciiHost ? uri.host : uri.spec;
--> --------------------

--> maximum size reached

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

Messung V0.5
C=91 H=94 G=92

¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

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