mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			2176 lines
		
	
	
	
		
			61 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			2176 lines
		
	
	
	
		
			61 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | 
						|
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
 | 
						|
 | 
						|
const lazy = {};
 | 
						|
 | 
						|
ChromeUtils.defineESModuleGetters(lazy, {
 | 
						|
  Downloads: "resource://gre/modules/Downloads.sys.mjs",
 | 
						|
  PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
 | 
						|
  ServiceWorkerCleanUp: "resource://gre/modules/ServiceWorkerCleanUp.sys.mjs",
 | 
						|
});
 | 
						|
 | 
						|
XPCOMUtils.defineLazyServiceGetter(
 | 
						|
  lazy,
 | 
						|
  "sas",
 | 
						|
  "@mozilla.org/storage/activity-service;1",
 | 
						|
  "nsIStorageActivityService"
 | 
						|
);
 | 
						|
XPCOMUtils.defineLazyServiceGetter(
 | 
						|
  lazy,
 | 
						|
  "TrackingDBService",
 | 
						|
  "@mozilla.org/tracking-db-service;1",
 | 
						|
  "nsITrackingDBService"
 | 
						|
);
 | 
						|
XPCOMUtils.defineLazyServiceGetter(
 | 
						|
  lazy,
 | 
						|
  "IdentityCredentialStorageService",
 | 
						|
  "@mozilla.org/browser/identity-credential-storage-service;1",
 | 
						|
  "nsIIdentityCredentialStorageService"
 | 
						|
);
 | 
						|
XPCOMUtils.defineLazyServiceGetter(
 | 
						|
  lazy,
 | 
						|
  "bounceTrackingProtection",
 | 
						|
  "@mozilla.org/bounce-tracking-protection;1",
 | 
						|
  "nsIBounceTrackingProtection"
 | 
						|
);
 | 
						|
 | 
						|
XPCOMUtils.defineLazyPreferenceGetter(
 | 
						|
  lazy,
 | 
						|
  "isBounceTrackingProtectionEnabled",
 | 
						|
  "privacy.bounceTrackingProtection.enabled",
 | 
						|
  false
 | 
						|
);
 | 
						|
 | 
						|
/**
 | 
						|
 * Test if host, OriginAttributes or principal belong to a baseDomain. Also
 | 
						|
 * considers partitioned storage by inspecting OriginAttributes partitionKey.
 | 
						|
 * @param options
 | 
						|
 * @param {string} [options.host] - Optional host to compare to base domain.
 | 
						|
 * @param {object} [options.originAttributes] - Optional origin attributes to
 | 
						|
 * inspect for aBaseDomain. If omitted, partitionKey will not be matched.
 | 
						|
 * @param {nsIPrincipal} [options.principal] - Optional principal to compare to
 | 
						|
 * base domain.
 | 
						|
 * @param {string} aBaseDomain - Domain to check for. Must be a valid, non-empty
 | 
						|
 * baseDomain string.
 | 
						|
 * @returns {boolean} Whether the host, originAttributes or principal matches
 | 
						|
 * the base domain.
 | 
						|
 */
 | 
						|
function hasBaseDomain(
 | 
						|
  { host = null, originAttributes = null, principal = null },
 | 
						|
  aBaseDomain
 | 
						|
) {
 | 
						|
  if (!aBaseDomain) {
 | 
						|
    throw new Error("Missing baseDomain.");
 | 
						|
  }
 | 
						|
  if (!host && !originAttributes && !principal) {
 | 
						|
    throw new Error(
 | 
						|
      "Missing host, originAttributes or principal to match with baseDomain."
 | 
						|
    );
 | 
						|
  }
 | 
						|
  if (principal && (host || originAttributes)) {
 | 
						|
    throw new Error(
 | 
						|
      "Can only pass either principal or host and originAttributes."
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  if (host && Services.eTLD.hasRootDomain(host, aBaseDomain)) {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  if (principal?.baseDomain == aBaseDomain) {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  originAttributes = originAttributes || principal?.originAttributes;
 | 
						|
  if (!originAttributes) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  return ChromeUtils.originAttributesMatchPattern(originAttributes, {
 | 
						|
    partitionKeyPattern: { baseDomain: aBaseDomain },
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Compute the base domain from a given host. This is a wrapper around
 | 
						|
 * Services.eTLD.getBaseDomainFromHost which also supports IP addresses and
 | 
						|
 * hosts such as "localhost" which are considered valid base domains for
 | 
						|
 * principals and data storage.
 | 
						|
 * @param {string} aDomainOrHost - Domain or host to be converted. May already
 | 
						|
 * be a valid base domain.
 | 
						|
 * @returns {string} Base domain of the given host. Returns aDomainOrHost if
 | 
						|
 * already a base domain.
 | 
						|
 */
 | 
						|
function getBaseDomainWithFallback(aDomainOrHost) {
 | 
						|
  let result = aDomainOrHost;
 | 
						|
  try {
 | 
						|
    result = Services.eTLD.getBaseDomainFromHost(aDomainOrHost);
 | 
						|
  } catch (e) {
 | 
						|
    if (
 | 
						|
      e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS ||
 | 
						|
      e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
 | 
						|
    ) {
 | 
						|
      // For these 2 expected errors, just take the host as the result.
 | 
						|
      // - NS_ERROR_HOST_IS_IP_ADDRESS: the host is in ipv4/ipv6.
 | 
						|
      // - NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS: not enough domain parts to extract.
 | 
						|
      result = aDomainOrHost;
 | 
						|
    } else {
 | 
						|
      throw e;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
// Here is a list of methods cleaners may implement. These methods must return a
 | 
						|
// Promise object.
 | 
						|
// * deleteAll() - this method _must_ exist. When called, it deletes all the
 | 
						|
//                 data owned by the cleaner.
 | 
						|
// * deleteByPrincipal() -  this method _must_ exist.
 | 
						|
// * deleteByBaseDomain() - this method _must_ exist.
 | 
						|
// * deleteByHost() - this method is implemented only if the cleaner knows
 | 
						|
//                    how to delete data by host + originAttributes pattern. If
 | 
						|
//                    not implemented, deleteAll() will be used as fallback.
 | 
						|
// * deleteByRange() - this method is implemented only if the cleaner knows how
 | 
						|
//                    to delete data by time range. It receives 2 time range
 | 
						|
//                    parameters: aFrom/aTo. If not implemented, deleteAll() is
 | 
						|
//                    used as fallback.
 | 
						|
// * deleteByLocalFiles() - this method removes data held for local files and
 | 
						|
//                          other hostless origins. If not implemented,
 | 
						|
//                          **no fallback is used**, as for a number of
 | 
						|
//                          cleaners, no such data will ever exist and
 | 
						|
//                          therefore clearing it does not make sense.
 | 
						|
// * deleteByOriginAttributes() - this method is implemented only if the cleaner
 | 
						|
//                                knows how to delete data by originAttributes
 | 
						|
//                                pattern.
 | 
						|
// * cleanupAfterDeletionAtShutdown() - this method is implemented only if the
 | 
						|
//                                      cleaner needs a separate step after
 | 
						|
//                                      deletion. No-op if not implemented.
 | 
						|
//                                      Currently called via
 | 
						|
//                                      Sanitizer.maybeSanitizeSessionPrincipals().
 | 
						|
 | 
						|
const CookieCleaner = {
 | 
						|
  deleteByLocalFiles(aOriginAttributes) {
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      Services.cookies.removeCookiesFromExactHost(
 | 
						|
        "",
 | 
						|
        JSON.stringify(aOriginAttributes)
 | 
						|
      );
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      Services.cookies.removeCookiesFromExactHost(
 | 
						|
        aHost,
 | 
						|
        JSON.stringify(aOriginAttributes)
 | 
						|
      );
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    // Fall back to clearing by host and OA pattern. This will over-clear, since
 | 
						|
    // any properties that are not explicitly set in aPrincipal.originAttributes
 | 
						|
    // will be wildcard matched.
 | 
						|
    return this.deleteByHost(aPrincipal.host, aPrincipal.originAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aDomain) {
 | 
						|
    Services.cookies.cookies
 | 
						|
      .filter(({ rawHost, originAttributes }) =>
 | 
						|
        hasBaseDomain({ host: rawHost, originAttributes }, aDomain)
 | 
						|
      )
 | 
						|
      .forEach(cookie => {
 | 
						|
        Services.cookies.removeCookiesFromExactHost(
 | 
						|
          cookie.rawHost,
 | 
						|
          JSON.stringify(cookie.originAttributes)
 | 
						|
        );
 | 
						|
      });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByRange(aFrom) {
 | 
						|
    return Services.cookies.removeAllSince(aFrom);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByOriginAttributes(aOriginAttributesString) {
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      try {
 | 
						|
        Services.cookies.removeCookiesWithOriginAttributes(
 | 
						|
          aOriginAttributesString
 | 
						|
        );
 | 
						|
      } catch (ex) {}
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      Services.cookies.removeAll();
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
// A cleaner for clearing cookie banner handling exceptions.
 | 
						|
const CookieBannerExceptionCleaner = {
 | 
						|
  async deleteAll() {
 | 
						|
    try {
 | 
						|
      Services.cookieBanners.removeAllDomainPrefs(false);
 | 
						|
    } catch (e) {
 | 
						|
      // Don't throw an error if the cookie banner handling is disabled.
 | 
						|
      if (e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
 | 
						|
        throw e;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByPrincipal(aPrincipal) {
 | 
						|
    try {
 | 
						|
      Services.cookieBanners.removeDomainPref(aPrincipal.URI, false);
 | 
						|
    } catch (e) {
 | 
						|
      // Don't throw an error if the cookie banner handling is disabled.
 | 
						|
      if (e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
 | 
						|
        throw e;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aDomain) {
 | 
						|
    try {
 | 
						|
      Services.cookieBanners.removeDomainPref(
 | 
						|
        Services.io.newURI("https://" + aDomain),
 | 
						|
        false
 | 
						|
      );
 | 
						|
    } catch (e) {
 | 
						|
      // Don't throw an error if the cookie banner handling is disabled.
 | 
						|
      if (e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
 | 
						|
        throw e;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    try {
 | 
						|
      let isPrivate =
 | 
						|
        !!aOriginAttributes.privateBrowsingId &&
 | 
						|
        aOriginAttributes.privateBrowsingId !==
 | 
						|
          Services.scriptSecurityManager.DEFAULT_PRIVATE_BROWSING_ID;
 | 
						|
 | 
						|
      Services.cookieBanners.removeDomainPref(
 | 
						|
        Services.io.newURI("https://" + aHost),
 | 
						|
        isPrivate
 | 
						|
      );
 | 
						|
    } catch (e) {
 | 
						|
      // Don't throw an error if the cookie banner handling is disabled.
 | 
						|
      if (e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
 | 
						|
        throw e;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
// A cleaner for cleaning cookie banner handling executed records.
 | 
						|
const CookieBannerExecutedRecordCleaner = {
 | 
						|
  async deleteAll() {
 | 
						|
    try {
 | 
						|
      Services.cookieBanners.removeAllExecutedRecords(false);
 | 
						|
    } catch (e) {
 | 
						|
      // Don't throw an error if the cookie banner handling is disabled.
 | 
						|
      if (e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
 | 
						|
        throw e;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByPrincipal(aPrincipal) {
 | 
						|
    try {
 | 
						|
      Services.cookieBanners.removeExecutedRecordForSite(
 | 
						|
        aPrincipal.baseDomain,
 | 
						|
        false
 | 
						|
      );
 | 
						|
    } catch (e) {
 | 
						|
      // Don't throw an error if the cookie banner handling is disabled.
 | 
						|
      if (e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
 | 
						|
        throw e;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aDomain) {
 | 
						|
    try {
 | 
						|
      Services.cookieBanners.removeExecutedRecordForSite(aDomain, false);
 | 
						|
    } catch (e) {
 | 
						|
      // Don't throw an error if the cookie banner handling is disabled.
 | 
						|
      if (e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
 | 
						|
        throw e;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    try {
 | 
						|
      let isPrivate =
 | 
						|
        !!aOriginAttributes.privateBrowsingId &&
 | 
						|
        aOriginAttributes.privateBrowsingId !==
 | 
						|
          Services.scriptSecurityManager.DEFAULT_PRIVATE_BROWSING_ID;
 | 
						|
 | 
						|
      Services.cookieBanners.removeExecutedRecordForSite(aHost, isPrivate);
 | 
						|
    } catch (e) {
 | 
						|
      // Don't throw error if the cookie banner handling is disabled.
 | 
						|
      if (e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
 | 
						|
        throw e;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
// A cleaner for cleaning fingerprinting protection states.
 | 
						|
const FingerprintingProtectionStateCleaner = {
 | 
						|
  async deleteAll() {
 | 
						|
    Services.rfp.cleanAllRandomKeys();
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByPrincipal(aPrincipal) {
 | 
						|
    Services.rfp.cleanRandomKeyByPrincipal(aPrincipal);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aDomain) {
 | 
						|
    Services.rfp.cleanRandomKeyByDomain(aDomain);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByHost(aHost, aOriginAttributesPattern) {
 | 
						|
    Services.rfp.cleanRandomKeyByHost(
 | 
						|
      aHost,
 | 
						|
      JSON.stringify(aOriginAttributesPattern)
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByOriginAttributes(aOriginAttributesString) {
 | 
						|
    Services.rfp.cleanRandomKeyByOriginAttributesPattern(
 | 
						|
      aOriginAttributesString
 | 
						|
    );
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const CertCleaner = {
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    let overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(
 | 
						|
      Ci.nsICertOverrideService
 | 
						|
    );
 | 
						|
 | 
						|
    overrideService.clearValidityOverride(aHost, -1, aOriginAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    return this.deleteByHost(aPrincipal.host, aPrincipal.originAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain) {
 | 
						|
    let overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(
 | 
						|
      Ci.nsICertOverrideService
 | 
						|
    );
 | 
						|
    overrideService
 | 
						|
      .getOverrides()
 | 
						|
      .filter(({ asciiHost }) =>
 | 
						|
        hasBaseDomain({ host: asciiHost }, aBaseDomain)
 | 
						|
      )
 | 
						|
      .forEach(({ asciiHost, port }) =>
 | 
						|
        overrideService.clearValidityOverride(asciiHost, port, {})
 | 
						|
      );
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    let overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(
 | 
						|
      Ci.nsICertOverrideService
 | 
						|
    );
 | 
						|
 | 
						|
    overrideService.clearAllOverrides();
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const NetworkCacheCleaner = {
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    // Delete data from both HTTP and HTTPS sites.
 | 
						|
    let httpURI = Services.io.newURI("http://" + aHost);
 | 
						|
    let httpsURI = Services.io.newURI("https://" + aHost);
 | 
						|
    let httpPrincipal = Services.scriptSecurityManager.createContentPrincipal(
 | 
						|
      httpURI,
 | 
						|
      aOriginAttributes
 | 
						|
    );
 | 
						|
    let httpsPrincipal = Services.scriptSecurityManager.createContentPrincipal(
 | 
						|
      httpsURI,
 | 
						|
      aOriginAttributes
 | 
						|
    );
 | 
						|
 | 
						|
    Services.cache2.clearOrigin(httpPrincipal);
 | 
						|
    Services.cache2.clearOrigin(httpsPrincipal);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain) {
 | 
						|
    Services.cache2.clearBaseDomain(aBaseDomain);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      Services.cache2.clearOrigin(aPrincipal);
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByOriginAttributes(aOriginAttributesString) {
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      Services.cache2.clearOriginAttributes(aOriginAttributesString);
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      Services.cache2.clear();
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const CSSCacheCleaner = {
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    // Delete data from both HTTP and HTTPS sites.
 | 
						|
    let httpURI = Services.io.newURI("http://" + aHost);
 | 
						|
    let httpsURI = Services.io.newURI("https://" + aHost);
 | 
						|
    let httpPrincipal = Services.scriptSecurityManager.createContentPrincipal(
 | 
						|
      httpURI,
 | 
						|
      aOriginAttributes
 | 
						|
    );
 | 
						|
    let httpsPrincipal = Services.scriptSecurityManager.createContentPrincipal(
 | 
						|
      httpsURI,
 | 
						|
      aOriginAttributes
 | 
						|
    );
 | 
						|
 | 
						|
    ChromeUtils.clearStyleSheetCacheByPrincipal(httpPrincipal);
 | 
						|
    ChromeUtils.clearStyleSheetCacheByPrincipal(httpsPrincipal);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByPrincipal(aPrincipal) {
 | 
						|
    ChromeUtils.clearStyleSheetCacheByPrincipal(aPrincipal);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain) {
 | 
						|
    ChromeUtils.clearStyleSheetCacheByBaseDomain(aBaseDomain);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    ChromeUtils.clearStyleSheetCache();
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const ImageCacheCleaner = {
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    let imageCache = Cc["@mozilla.org/image/tools;1"]
 | 
						|
      .getService(Ci.imgITools)
 | 
						|
      .getImgCacheForDocument(null);
 | 
						|
 | 
						|
    // Delete data from both HTTP and HTTPS sites.
 | 
						|
    let httpURI = Services.io.newURI("http://" + aHost);
 | 
						|
    let httpsURI = Services.io.newURI("https://" + aHost);
 | 
						|
    let httpPrincipal = Services.scriptSecurityManager.createContentPrincipal(
 | 
						|
      httpURI,
 | 
						|
      aOriginAttributes
 | 
						|
    );
 | 
						|
    let httpsPrincipal = Services.scriptSecurityManager.createContentPrincipal(
 | 
						|
      httpsURI,
 | 
						|
      aOriginAttributes
 | 
						|
    );
 | 
						|
 | 
						|
    imageCache.removeEntriesFromPrincipalInAllProcesses(httpPrincipal);
 | 
						|
    imageCache.removeEntriesFromPrincipalInAllProcesses(httpsPrincipal);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByPrincipal(aPrincipal) {
 | 
						|
    let imageCache = Cc["@mozilla.org/image/tools;1"]
 | 
						|
      .getService(Ci.imgITools)
 | 
						|
      .getImgCacheForDocument(null);
 | 
						|
    imageCache.removeEntriesFromPrincipalInAllProcesses(aPrincipal);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain) {
 | 
						|
    let imageCache = Cc["@mozilla.org/image/tools;1"]
 | 
						|
      .getService(Ci.imgITools)
 | 
						|
      .getImgCacheForDocument(null);
 | 
						|
    imageCache.removeEntriesFromBaseDomainInAllProcesses(aBaseDomain);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      let imageCache = Cc["@mozilla.org/image/tools;1"]
 | 
						|
        .getService(Ci.imgITools)
 | 
						|
        .getImgCacheForDocument(null);
 | 
						|
      imageCache.clearCache(false); // true=chrome, false=content
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const DownloadsCleaner = {
 | 
						|
  async _deleteInternal({ hostOrBaseDomain, principal, originAttributes }) {
 | 
						|
    originAttributes = originAttributes || principal?.originAttributes || {};
 | 
						|
 | 
						|
    let list = await lazy.Downloads.getList(lazy.Downloads.ALL);
 | 
						|
    list.removeFinished(({ source }) => {
 | 
						|
      if (
 | 
						|
        "userContextId" in originAttributes &&
 | 
						|
        "userContextId" in source &&
 | 
						|
        originAttributes.userContextId != source.userContextId
 | 
						|
      ) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      if (
 | 
						|
        "privateBrowsingId" in originAttributes &&
 | 
						|
        !!originAttributes.privateBrowsingId != source.isPrivate
 | 
						|
      ) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      let entryURI = Services.io.newURI(source.url);
 | 
						|
      if (hostOrBaseDomain) {
 | 
						|
        return Services.eTLD.hasRootDomain(entryURI.host, hostOrBaseDomain);
 | 
						|
      }
 | 
						|
      if (principal) {
 | 
						|
        return principal.equalsURI(entryURI);
 | 
						|
      }
 | 
						|
      return false;
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    // Clearing by host also clears associated subdomains.
 | 
						|
    return this._deleteInternal({
 | 
						|
      hostOrBaseDomain: aHost,
 | 
						|
      originAttributes: aOriginAttributes,
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    return this._deleteInternal({ principal: aPrincipal });
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain) {
 | 
						|
    return this._deleteInternal({ hostOrBaseDomain: aBaseDomain });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByRange(aFrom, aTo) {
 | 
						|
    // Convert microseconds back to milliseconds for date comparisons.
 | 
						|
    let rangeBeginMs = aFrom / 1000;
 | 
						|
    let rangeEndMs = aTo / 1000;
 | 
						|
 | 
						|
    return lazy.Downloads.getList(lazy.Downloads.ALL).then(aList => {
 | 
						|
      aList.removeFinished(
 | 
						|
        aDownload =>
 | 
						|
          aDownload.startTime >= rangeBeginMs &&
 | 
						|
          aDownload.startTime <= rangeEndMs
 | 
						|
      );
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    return lazy.Downloads.getList(lazy.Downloads.ALL).then(aList => {
 | 
						|
      aList.removeFinished(null);
 | 
						|
    });
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const PasswordsCleaner = {
 | 
						|
  deleteByHost(aHost) {
 | 
						|
    // Clearing by host also clears associated subdomains.
 | 
						|
    return this._deleteInternal(aLogin =>
 | 
						|
      Services.eTLD.hasRootDomain(aLogin.hostname, aHost)
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    // Login origins don't contain any origin attributes.
 | 
						|
    return this._deleteInternal(
 | 
						|
      aLogin => aLogin.origin == aPrincipal.originNoSuffix
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByBaseDomain(aBaseDomain) {
 | 
						|
    return this._deleteInternal(aLogin =>
 | 
						|
      Services.eTLD.hasRootDomain(aLogin.hostname, aBaseDomain)
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    return this._deleteInternal(() => true);
 | 
						|
  },
 | 
						|
 | 
						|
  async _deleteInternal(aCb) {
 | 
						|
    try {
 | 
						|
      let logins = await Services.logins.getAllLogins();
 | 
						|
      for (let login of logins) {
 | 
						|
        if (aCb(login)) {
 | 
						|
          Services.logins.removeLogin(login);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    } catch (ex) {
 | 
						|
      // XXXehsan: is there a better way to do this rather than this
 | 
						|
      // hacky comparison?
 | 
						|
      if (
 | 
						|
        !ex.message.includes("User canceled Master Password entry") &&
 | 
						|
        ex.result != Cr.NS_ERROR_NOT_IMPLEMENTED
 | 
						|
      ) {
 | 
						|
        throw new Error("Exception occured in clearing passwords: " + ex);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const MediaDevicesCleaner = {
 | 
						|
  async deleteByRange(aFrom) {
 | 
						|
    let mediaMgr = Cc["@mozilla.org/mediaManagerService;1"].getService(
 | 
						|
      Ci.nsIMediaManagerService
 | 
						|
    );
 | 
						|
    mediaMgr.sanitizeDeviceIds(aFrom);
 | 
						|
  },
 | 
						|
 | 
						|
  // TODO: We should call the MediaManager to clear by principal, rather than
 | 
						|
  // over-clearing for user requests or bailing out for programmatic calls.
 | 
						|
  async deleteByPrincipal(aPrincipal, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  // TODO: Same as above, but for base domain.
 | 
						|
  async deleteByBaseDomain(aBaseDomain, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    let mediaMgr = Cc["@mozilla.org/mediaManagerService;1"].getService(
 | 
						|
      Ci.nsIMediaManagerService
 | 
						|
    );
 | 
						|
    mediaMgr.sanitizeDeviceIds(null);
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const QuotaCleaner = {
 | 
						|
  /**
 | 
						|
   * Clear quota storage for matching principals.
 | 
						|
   * @param {function} filterFn - Filter function which is passed a principal.
 | 
						|
   * Return true to clear storage for given principal or false to skip it.
 | 
						|
   * @returns {Promise} - Resolves once all matching items have been cleared.
 | 
						|
   * Rejects on error.
 | 
						|
   */
 | 
						|
  async _qmsClearStoragesForPrincipalsMatching(filterFn) {
 | 
						|
    // Clearing quota storage by first getting all entry origins and then
 | 
						|
    // iterating over them is not ideal, since we can not ensure an entirely
 | 
						|
    // consistent clearing state. Between fetching the origins and clearing
 | 
						|
    // them, additional entries could be added. This means we could end up with
 | 
						|
    // stray entries after the clearing operation. To fix this we would need to
 | 
						|
    // move the clearing code to the QuotaManager itself which could either
 | 
						|
    // prevent new writes while clearing or clean up any additional entries
 | 
						|
    // which get written during the clearing operation.
 | 
						|
    // Performance is also not ideal, since we iterate over storage multiple
 | 
						|
    // times for this two step process.
 | 
						|
    // See Bug 1719195.
 | 
						|
    let origins = await new Promise((resolve, reject) => {
 | 
						|
      Services.qms.listOrigins().callback = request => {
 | 
						|
        if (request.resultCode != Cr.NS_OK) {
 | 
						|
          reject({ message: "Deleting quota storages failed" });
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        resolve(request.result);
 | 
						|
      };
 | 
						|
    });
 | 
						|
 | 
						|
    let clearPromises = origins
 | 
						|
      // Parse origins into principals.
 | 
						|
      .map(Services.scriptSecurityManager.createContentPrincipalFromOrigin)
 | 
						|
      // Filter out principals that don't match the filterFn.
 | 
						|
      .filter(filterFn)
 | 
						|
      // Clear quota storage by principal and collect the promises.
 | 
						|
      .map(
 | 
						|
        principal =>
 | 
						|
          new Promise((resolve, reject) => {
 | 
						|
            let clearRequest =
 | 
						|
              Services.qms.clearStoragesForPrincipal(principal);
 | 
						|
            clearRequest.callback = () => {
 | 
						|
              if (clearRequest.resultCode != Cr.NS_OK) {
 | 
						|
                reject({ message: "Deleting quota storages failed" });
 | 
						|
                return;
 | 
						|
              }
 | 
						|
              resolve();
 | 
						|
            };
 | 
						|
          })
 | 
						|
      );
 | 
						|
    return Promise.all(clearPromises);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    // localStorage: The legacy LocalStorage implementation that will
 | 
						|
    // eventually be removed depends on this observer notification to clear by
 | 
						|
    // principal.
 | 
						|
    Services.obs.notifyObservers(
 | 
						|
      null,
 | 
						|
      "extension:purge-localStorage",
 | 
						|
      aPrincipal.host
 | 
						|
    );
 | 
						|
 | 
						|
    // Clear sessionStorage
 | 
						|
    Services.sessionStorage.clearStoragesForOrigin(aPrincipal);
 | 
						|
 | 
						|
    // ServiceWorkers: they must be removed before cleaning QuotaManager.
 | 
						|
    return lazy.ServiceWorkerCleanUp.removeFromPrincipal(aPrincipal)
 | 
						|
      .then(
 | 
						|
        _ => /* exceptionThrown = */ false,
 | 
						|
        _ => /* exceptionThrown = */ true
 | 
						|
      )
 | 
						|
      .then(exceptionThrown => {
 | 
						|
        // QuotaManager: In the event of a failure, we call reject to propagate
 | 
						|
        // the error upwards.
 | 
						|
        return new Promise((aResolve, aReject) => {
 | 
						|
          let req = Services.qms.clearStoragesForPrincipal(aPrincipal);
 | 
						|
          req.callback = () => {
 | 
						|
            if (exceptionThrown || req.resultCode != Cr.NS_OK) {
 | 
						|
              aReject({ message: "Delete by principal failed" });
 | 
						|
            } else {
 | 
						|
              aResolve();
 | 
						|
            }
 | 
						|
          };
 | 
						|
        });
 | 
						|
      });
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain) {
 | 
						|
    // localStorage: The legacy LocalStorage implementation that will
 | 
						|
    // eventually be removed depends on this observer notification to clear by
 | 
						|
    // host.  Some other subsystems like Reporting headers depend on this too.
 | 
						|
    Services.obs.notifyObservers(
 | 
						|
      null,
 | 
						|
      "extension:purge-localStorage",
 | 
						|
      aBaseDomain
 | 
						|
    );
 | 
						|
 | 
						|
    // Clear sessionStorage
 | 
						|
    Services.obs.notifyObservers(
 | 
						|
      null,
 | 
						|
      "browser:purge-sessionStorage",
 | 
						|
      aBaseDomain
 | 
						|
    );
 | 
						|
 | 
						|
    // Clear third-party storage partitioned under aBaseDomain.
 | 
						|
    // This notification is forwarded via the StorageObserver and consumed only
 | 
						|
    // by the SessionStorageManager and (legacy) LocalStorageManager.
 | 
						|
    // There is a similar (legacy) notification "clear-origin-attributes-data"
 | 
						|
    // which additionally clears data across various other storages unrelated to
 | 
						|
    // the QuotaCleaner.
 | 
						|
    Services.obs.notifyObservers(
 | 
						|
      null,
 | 
						|
      "dom-storage:clear-origin-attributes-data",
 | 
						|
      JSON.stringify({ partitionKeyPattern: { baseDomain: aBaseDomain } })
 | 
						|
    );
 | 
						|
 | 
						|
    // ServiceWorkers must be removed before cleaning QuotaManager. We store
 | 
						|
    // potential errors so we can re-throw later, once all operations have
 | 
						|
    // completed.
 | 
						|
    let swCleanupError;
 | 
						|
    try {
 | 
						|
      await lazy.ServiceWorkerCleanUp.removeFromBaseDomain(aBaseDomain);
 | 
						|
    } catch (error) {
 | 
						|
      swCleanupError = error;
 | 
						|
    }
 | 
						|
 | 
						|
    await this._qmsClearStoragesForPrincipalsMatching(principal =>
 | 
						|
      hasBaseDomain({ principal }, aBaseDomain)
 | 
						|
    );
 | 
						|
 | 
						|
    // Re-throw any service worker cleanup errors.
 | 
						|
    if (swCleanupError) {
 | 
						|
      throw swCleanupError;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByHost(aHost) {
 | 
						|
    // XXX: The aOriginAttributes is expected to always be empty({}). Maybe have
 | 
						|
    // a debug assertion here to ensure that?
 | 
						|
 | 
						|
    // localStorage: The legacy LocalStorage implementation that will
 | 
						|
    // eventually be removed depends on this observer notification to clear by
 | 
						|
    // host.  Some other subsystems like Reporting headers depend on this too.
 | 
						|
    Services.obs.notifyObservers(null, "extension:purge-localStorage", aHost);
 | 
						|
 | 
						|
    // Clear sessionStorage
 | 
						|
    Services.obs.notifyObservers(null, "browser:purge-sessionStorage", aHost);
 | 
						|
 | 
						|
    // ServiceWorkers must be removed before cleaning QuotaManager. We store any
 | 
						|
    // errors so we can re-throw later once all operations have completed.
 | 
						|
    let swCleanupError;
 | 
						|
    try {
 | 
						|
      await lazy.ServiceWorkerCleanUp.removeFromHost(aHost);
 | 
						|
    } catch (error) {
 | 
						|
      swCleanupError = error;
 | 
						|
    }
 | 
						|
 | 
						|
    await this._qmsClearStoragesForPrincipalsMatching(principal => {
 | 
						|
      try {
 | 
						|
        // deleteByHost has the semantics that "foo.example.com" should be
 | 
						|
        // wiped if we are provided an aHost of "example.com".
 | 
						|
        return Services.eTLD.hasRootDomain(principal.host, aHost);
 | 
						|
      } catch (e) {
 | 
						|
        // There is no host for the given principal.
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    // Re-throw any service worker cleanup errors.
 | 
						|
    if (swCleanupError) {
 | 
						|
      throw swCleanupError;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByRange(aFrom, aTo) {
 | 
						|
    let principals = lazy.sas
 | 
						|
      .getActiveOrigins(aFrom, aTo)
 | 
						|
      .QueryInterface(Ci.nsIArray);
 | 
						|
 | 
						|
    let promises = [];
 | 
						|
    for (let i = 0; i < principals.length; ++i) {
 | 
						|
      let principal = principals.queryElementAt(i, Ci.nsIPrincipal);
 | 
						|
 | 
						|
      if (
 | 
						|
        !principal.schemeIs("http") &&
 | 
						|
        !principal.schemeIs("https") &&
 | 
						|
        !principal.schemeIs("file")
 | 
						|
      ) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      promises.push(this.deleteByPrincipal(principal));
 | 
						|
    }
 | 
						|
 | 
						|
    return Promise.all(promises);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByOriginAttributes(aOriginAttributesString) {
 | 
						|
    // The legacy LocalStorage implementation that will eventually be removed.
 | 
						|
    // And it should've been cleared while notifying observers with
 | 
						|
    // clear-origin-attributes-data.
 | 
						|
 | 
						|
    return lazy.ServiceWorkerCleanUp.removeFromOriginAttributes(
 | 
						|
      aOriginAttributesString
 | 
						|
    )
 | 
						|
      .then(
 | 
						|
        _ => /* exceptionThrown = */ false,
 | 
						|
        _ => /* exceptionThrown = */ true
 | 
						|
      )
 | 
						|
      .then(() => {
 | 
						|
        // QuotaManager: In the event of a failure, we call reject to propagate
 | 
						|
        // the error upwards.
 | 
						|
        return new Promise((aResolve, aReject) => {
 | 
						|
          let req = Services.qms.clearStoragesForOriginAttributesPattern(
 | 
						|
            aOriginAttributesString
 | 
						|
          );
 | 
						|
          req.callback = () => {
 | 
						|
            if (req.resultCode == Cr.NS_OK) {
 | 
						|
              aResolve();
 | 
						|
            } else {
 | 
						|
              aReject({ message: "Delete by origin attributes failed" });
 | 
						|
            }
 | 
						|
          };
 | 
						|
        });
 | 
						|
      });
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    // localStorage
 | 
						|
    Services.obs.notifyObservers(null, "extension:purge-localStorage");
 | 
						|
 | 
						|
    // sessionStorage
 | 
						|
    Services.obs.notifyObservers(null, "browser:purge-sessionStorage");
 | 
						|
 | 
						|
    // ServiceWorkers must be removed before cleaning QuotaManager. We store any
 | 
						|
    // errors so we can re-throw later once all operations have completed.
 | 
						|
    let swCleanupError;
 | 
						|
    try {
 | 
						|
      await lazy.ServiceWorkerCleanUp.removeAll();
 | 
						|
    } catch (error) {
 | 
						|
      swCleanupError = error;
 | 
						|
    }
 | 
						|
 | 
						|
    await this._qmsClearStoragesForPrincipalsMatching(
 | 
						|
      principal =>
 | 
						|
        principal.schemeIs("http") ||
 | 
						|
        principal.schemeIs("https") ||
 | 
						|
        principal.schemeIs("file")
 | 
						|
    );
 | 
						|
 | 
						|
    // Re-throw any service worker cleanup errors.
 | 
						|
    if (swCleanupError) {
 | 
						|
      throw swCleanupError;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async cleanupAfterDeletionAtShutdown() {
 | 
						|
    const toBeRemovedDir = PathUtils.join(
 | 
						|
      PathUtils.profileDir,
 | 
						|
      Services.prefs.getStringPref("dom.quotaManager.storageName"),
 | 
						|
      "to-be-removed"
 | 
						|
    );
 | 
						|
 | 
						|
    if (
 | 
						|
      !AppConstants.MOZ_BACKGROUNDTASKS ||
 | 
						|
      !Services.prefs.getBoolPref("dom.quotaManager.backgroundTask.enabled")
 | 
						|
    ) {
 | 
						|
      await IOUtils.remove(toBeRemovedDir, { recursive: true });
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    const runner = Cc["@mozilla.org/backgroundtasksrunner;1"].getService(
 | 
						|
      Ci.nsIBackgroundTasksRunner
 | 
						|
    );
 | 
						|
 | 
						|
    runner.removeDirectoryInDetachedProcess(
 | 
						|
      toBeRemovedDir,
 | 
						|
      "",
 | 
						|
      "0",
 | 
						|
      "*", // wildcard
 | 
						|
      "Quota"
 | 
						|
    );
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const PredictorNetworkCleaner = {
 | 
						|
  async deleteAll() {
 | 
						|
    // Predictive network data - like cache, no way to clear this per
 | 
						|
    // domain, so just trash it all
 | 
						|
    let np = Cc["@mozilla.org/network/predictor;1"].getService(
 | 
						|
      Ci.nsINetworkPredictor
 | 
						|
    );
 | 
						|
    np.reset();
 | 
						|
  },
 | 
						|
 | 
						|
  // TODO: We should call the NetworkPredictor to clear by principal, rather
 | 
						|
  // than over-clearing for user requests or bailing out for programmatic calls.
 | 
						|
  async deleteByPrincipal(aPrincipal, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  // TODO: Same as above, but for base domain.
 | 
						|
  async deleteByBaseDomain(aBaseDomain, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const PushNotificationsCleaner = {
 | 
						|
  /**
 | 
						|
   * Clear entries for aDomain including subdomains of aDomain.
 | 
						|
   * @param {string} aDomain - Domain to clear data for.
 | 
						|
   * @returns {Promise} a promise which resolves once data has been cleared.
 | 
						|
   */
 | 
						|
  _deleteByRootDomain(aDomain) {
 | 
						|
    if (!Services.prefs.getBoolPref("dom.push.enabled", false)) {
 | 
						|
      return Promise.resolve();
 | 
						|
    }
 | 
						|
 | 
						|
    return new Promise((aResolve, aReject) => {
 | 
						|
      let push = Cc["@mozilla.org/push/Service;1"].getService(
 | 
						|
        Ci.nsIPushService
 | 
						|
      );
 | 
						|
      // ClearForDomain also clears subdomains.
 | 
						|
      push.clearForDomain(aDomain, aStatus => {
 | 
						|
        if (!Components.isSuccessCode(aStatus)) {
 | 
						|
          aReject();
 | 
						|
        } else {
 | 
						|
          aResolve();
 | 
						|
        }
 | 
						|
      });
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByHost(aHost) {
 | 
						|
    // Will also clear entries for subdomains of aHost. Data is cleared across
 | 
						|
    // all origin attributes.
 | 
						|
    return this._deleteByRootDomain(aHost);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    // Will also clear entries for subdomains of the principal host. Data is
 | 
						|
    // cleared across all origin attributes.
 | 
						|
    return this._deleteByRootDomain(aPrincipal.host);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByBaseDomain(aBaseDomain) {
 | 
						|
    return this._deleteByRootDomain(aBaseDomain);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    if (!Services.prefs.getBoolPref("dom.push.enabled", false)) {
 | 
						|
      return Promise.resolve();
 | 
						|
    }
 | 
						|
 | 
						|
    return new Promise((aResolve, aReject) => {
 | 
						|
      let push = Cc["@mozilla.org/push/Service;1"].getService(
 | 
						|
        Ci.nsIPushService
 | 
						|
      );
 | 
						|
      push.clearForDomain("*", aStatus => {
 | 
						|
        if (!Components.isSuccessCode(aStatus)) {
 | 
						|
          aReject();
 | 
						|
        } else {
 | 
						|
          aResolve();
 | 
						|
        }
 | 
						|
      });
 | 
						|
    });
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const StorageAccessCleaner = {
 | 
						|
  // This is a special function to implement deleteUserInteractionForClearingHistory.
 | 
						|
  async deleteExceptPrincipals(aPrincipalsWithStorage, aFrom) {
 | 
						|
    // We compare by base domain in order to simulate the behavior
 | 
						|
    // from purging, Consider a scenario where the user is logged
 | 
						|
    // into sub.example.com but the cookies are on example.com. In this
 | 
						|
    // case, we will remove the user interaction for sub.example.com
 | 
						|
    // because its principal does not match the one with storage.
 | 
						|
    let baseDomainsWithStorage = new Set();
 | 
						|
    for (let principal of aPrincipalsWithStorage) {
 | 
						|
      baseDomainsWithStorage.add(principal.baseDomain);
 | 
						|
    }
 | 
						|
    for (let perm of Services.perms.getAllByTypeSince(
 | 
						|
      "storageAccessAPI",
 | 
						|
      // The permission manager uses milliseconds instead of microseconds
 | 
						|
      aFrom / 1000
 | 
						|
    )) {
 | 
						|
      if (!baseDomainsWithStorage.has(perm.principal.baseDomain)) {
 | 
						|
        Services.perms.removePermission(perm);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByPrincipal(aPrincipal) {
 | 
						|
    return Services.perms.removeFromPrincipal(aPrincipal, "storageAccessAPI");
 | 
						|
  },
 | 
						|
 | 
						|
  _deleteInternal(filter) {
 | 
						|
    Services.perms.all
 | 
						|
      .filter(({ type }) => type == "storageAccessAPI")
 | 
						|
      .filter(filter)
 | 
						|
      .forEach(perm => {
 | 
						|
        try {
 | 
						|
          Services.perms.removePermission(perm);
 | 
						|
        } catch (ex) {
 | 
						|
          console.error(ex);
 | 
						|
        }
 | 
						|
      });
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByHost(aHost) {
 | 
						|
    // Clearing by host also clears associated subdomains.
 | 
						|
    this._deleteInternal(({ principal }) => {
 | 
						|
      let toBeRemoved = false;
 | 
						|
      try {
 | 
						|
        toBeRemoved = Services.eTLD.hasRootDomain(principal.host, aHost);
 | 
						|
      } catch (ex) {}
 | 
						|
      return toBeRemoved;
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain) {
 | 
						|
    this._deleteInternal(
 | 
						|
      ({ principal }) => principal.baseDomain == aBaseDomain
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByRange(aFrom) {
 | 
						|
    Services.perms.removeByTypeSince("storageAccessAPI", aFrom / 1000);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    Services.perms.removeByType("storageAccessAPI");
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const HistoryCleaner = {
 | 
						|
  deleteByHost(aHost) {
 | 
						|
    if (!AppConstants.MOZ_PLACES) {
 | 
						|
      return Promise.resolve();
 | 
						|
    }
 | 
						|
    return lazy.PlacesUtils.history.removeByFilter({ host: "." + aHost });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    if (!AppConstants.MOZ_PLACES) {
 | 
						|
      return Promise.resolve();
 | 
						|
    }
 | 
						|
    return lazy.PlacesUtils.history.removeByFilter({ host: aPrincipal.host });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByBaseDomain(aBaseDomain) {
 | 
						|
    return this.deleteByHost(aBaseDomain, {});
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByRange(aFrom, aTo) {
 | 
						|
    if (!AppConstants.MOZ_PLACES) {
 | 
						|
      return Promise.resolve();
 | 
						|
    }
 | 
						|
    return lazy.PlacesUtils.history.removeVisitsByFilter({
 | 
						|
      beginDate: new Date(aFrom / 1000),
 | 
						|
      endDate: new Date(aTo / 1000),
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    if (!AppConstants.MOZ_PLACES) {
 | 
						|
      return Promise.resolve();
 | 
						|
    }
 | 
						|
    return lazy.PlacesUtils.history.clear();
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const SessionHistoryCleaner = {
 | 
						|
  async deleteByHost(aHost) {
 | 
						|
    // Session storage and history also clear subdomains of aHost.
 | 
						|
    Services.obs.notifyObservers(null, "browser:purge-sessionStorage", aHost);
 | 
						|
    Services.obs.notifyObservers(
 | 
						|
      null,
 | 
						|
      "browser:purge-session-history-for-domain",
 | 
						|
      aHost
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    return this.deleteByHost(aPrincipal.host, aPrincipal.originAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByBaseDomain(aBaseDomain) {
 | 
						|
    return this.deleteByHost(aBaseDomain, {});
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByRange(aFrom) {
 | 
						|
    Services.obs.notifyObservers(
 | 
						|
      null,
 | 
						|
      "browser:purge-session-history",
 | 
						|
      String(aFrom)
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    Services.obs.notifyObservers(null, "browser:purge-session-history");
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const AuthTokensCleaner = {
 | 
						|
  // TODO: Bug 1726742
 | 
						|
  async deleteByPrincipal(aPrincipal, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  // TODO: Bug 1726742
 | 
						|
  async deleteByBaseDomain(aBaseDomain, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
 | 
						|
      Ci.nsISecretDecoderRing
 | 
						|
    );
 | 
						|
    sdr.logoutAndTeardown();
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const AuthCacheCleaner = {
 | 
						|
  // TODO: Bug 1726743
 | 
						|
  async deleteByPrincipal(aPrincipal, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  // TODO: Bug 1726743
 | 
						|
  async deleteByBaseDomain(aBaseDomain, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      Services.obs.notifyObservers(null, "net:clear-active-logins");
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const PermissionsCleaner = {
 | 
						|
  /**
 | 
						|
   * Delete permissions by either base domain or host.
 | 
						|
   * Clearing by host also clears associated subdomains.
 | 
						|
   * For example, clearing "example.com" will also clear permissions for
 | 
						|
   * "test.example.com" and "another.test.example.com".
 | 
						|
   * @param options
 | 
						|
   * @param {string} options.baseDomain - Base domain to delete permissions for.
 | 
						|
   * @param {string} options.host - Host to delete permissions for.
 | 
						|
   */
 | 
						|
  async _deleteInternal({ baseDomain, host }) {
 | 
						|
    for (let perm of Services.perms.all) {
 | 
						|
      let toBeRemoved;
 | 
						|
 | 
						|
      if (baseDomain) {
 | 
						|
        toBeRemoved = perm.principal.baseDomain == baseDomain;
 | 
						|
      } else {
 | 
						|
        try {
 | 
						|
          toBeRemoved = Services.eTLD.hasRootDomain(perm.principal.host, host);
 | 
						|
        } catch (ex) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (
 | 
						|
        !toBeRemoved &&
 | 
						|
        (perm.type.startsWith("3rdPartyStorage^") ||
 | 
						|
          perm.type.startsWith("3rdPartyFrameStorage^"))
 | 
						|
      ) {
 | 
						|
        let parts = perm.type.split("^");
 | 
						|
        let uri;
 | 
						|
        try {
 | 
						|
          uri = Services.io.newURI(parts[1]);
 | 
						|
        } catch (ex) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
 | 
						|
        toBeRemoved = Services.eTLD.hasRootDomain(uri.host, baseDomain || host);
 | 
						|
      }
 | 
						|
 | 
						|
      if (!toBeRemoved) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      try {
 | 
						|
        Services.perms.removePermission(perm);
 | 
						|
      } catch (ex) {
 | 
						|
        // Ignore entry
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByHost(aHost) {
 | 
						|
    return this._deleteInternal({ host: aHost });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    return this.deleteByHost(aPrincipal.host, aPrincipal.originAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByBaseDomain(aBaseDomain) {
 | 
						|
    return this._deleteInternal({ baseDomain: aBaseDomain });
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByRange(aFrom) {
 | 
						|
    Services.perms.removeAllSince(aFrom / 1000);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByOriginAttributes(aOriginAttributesString) {
 | 
						|
    Services.perms.removePermissionsWithAttributes(aOriginAttributesString);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    Services.perms.removeAll();
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const PreferencesCleaner = {
 | 
						|
  deleteByHost(aHost) {
 | 
						|
    // Also clears subdomains of aHost.
 | 
						|
    return new Promise((aResolve, aReject) => {
 | 
						|
      let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
 | 
						|
        Ci.nsIContentPrefService2
 | 
						|
      );
 | 
						|
      cps2.removeBySubdomain(aHost, null, {
 | 
						|
        handleCompletion: aReason => {
 | 
						|
          if (aReason === cps2.COMPLETE_ERROR) {
 | 
						|
            aReject();
 | 
						|
          } else {
 | 
						|
            aResolve();
 | 
						|
          }
 | 
						|
        },
 | 
						|
        handleError() {},
 | 
						|
      });
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    return this.deleteByHost(aPrincipal.host, aPrincipal.originAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByBaseDomain(aBaseDomain) {
 | 
						|
    return this.deleteByHost(aBaseDomain, {});
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByRange(aFrom) {
 | 
						|
    let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
 | 
						|
      Ci.nsIContentPrefService2
 | 
						|
    );
 | 
						|
    cps2.removeAllDomainsSince(aFrom / 1000, null);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
 | 
						|
      Ci.nsIContentPrefService2
 | 
						|
    );
 | 
						|
    cps2.removeAllDomains(null);
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const ClientAuthRememberCleaner = {
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    let cars = Cc[
 | 
						|
      "@mozilla.org/security/clientAuthRememberService;1"
 | 
						|
    ].getService(Ci.nsIClientAuthRememberService);
 | 
						|
 | 
						|
    cars.deleteDecisionsByHost(aHost, aOriginAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    return this.deleteByHost(aPrincipal.host, aPrincipal.originAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aDomain) {
 | 
						|
    let cars = Cc[
 | 
						|
      "@mozilla.org/security/clientAuthRememberService;1"
 | 
						|
    ].getService(Ci.nsIClientAuthRememberService);
 | 
						|
 | 
						|
    cars
 | 
						|
      .getDecisions()
 | 
						|
      .filter(({ asciiHost, entryKey }) => {
 | 
						|
        // Get the origin attributes which are in the third component of the
 | 
						|
        // entryKey. ',' is used as the delimiter.
 | 
						|
        let originSuffixEncoded = entryKey.split(",")[2];
 | 
						|
        let originAttributes;
 | 
						|
 | 
						|
        if (originSuffixEncoded) {
 | 
						|
          try {
 | 
						|
            // Decoding the suffix or parsing the origin attributes can fail. In
 | 
						|
            // this case we won't match the partitionKey, but we can still match
 | 
						|
            // the asciiHost.
 | 
						|
            let originSuffix = decodeURIComponent(originSuffixEncoded);
 | 
						|
            originAttributes =
 | 
						|
              ChromeUtils.CreateOriginAttributesFromOriginSuffix(originSuffix);
 | 
						|
          } catch (e) {
 | 
						|
            console.error(e);
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        return hasBaseDomain(
 | 
						|
          {
 | 
						|
            host: asciiHost,
 | 
						|
            originAttributes,
 | 
						|
          },
 | 
						|
          aDomain
 | 
						|
        );
 | 
						|
      })
 | 
						|
      .forEach(({ entryKey }) => cars.forgetRememberedDecision(entryKey));
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    let cars = Cc[
 | 
						|
      "@mozilla.org/security/clientAuthRememberService;1"
 | 
						|
    ].getService(Ci.nsIClientAuthRememberService);
 | 
						|
    cars.clearRememberedDecisions();
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const HSTSCleaner = {
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    let sss = Cc["@mozilla.org/ssservice;1"].getService(
 | 
						|
      Ci.nsISiteSecurityService
 | 
						|
    );
 | 
						|
    let uri = Services.io.newURI("https://" + aHost);
 | 
						|
    sss.resetState(
 | 
						|
      uri,
 | 
						|
      aOriginAttributes,
 | 
						|
      Ci.nsISiteSecurityService.RootDomain
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Adds brackets to a site if it's an IPv6 address.
 | 
						|
   * @param {string} aSite - (schemeless) site which may be an IPv6.
 | 
						|
   * @returns {string} bracketed IPv6 or site if site is not an IPv6.
 | 
						|
   */
 | 
						|
  _maybeFixIpv6Site(aSite) {
 | 
						|
    // Not an IPv6 or already has brackets.
 | 
						|
    if (!aSite.includes(":") || aSite[0] == "[") {
 | 
						|
      return aSite;
 | 
						|
    }
 | 
						|
    return `[${aSite}]`;
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    return this.deleteByHost(aPrincipal.host, aPrincipal.originAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aDomain) {
 | 
						|
    let sss = Cc["@mozilla.org/ssservice;1"].getService(
 | 
						|
      Ci.nsISiteSecurityService
 | 
						|
    );
 | 
						|
 | 
						|
    // Add brackets to IPv6 sites to ensure URI creation succeeds.
 | 
						|
    let uri = Services.io.newURI("https://" + this._maybeFixIpv6Site(aDomain));
 | 
						|
    sss.resetState(uri, {}, Ci.nsISiteSecurityService.BaseDomain);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    // Clear site security settings - no support for ranges in this
 | 
						|
    // interface either, so we clearAll().
 | 
						|
    let sss = Cc["@mozilla.org/ssservice;1"].getService(
 | 
						|
      Ci.nsISiteSecurityService
 | 
						|
    );
 | 
						|
    sss.clearAll();
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const EMECleaner = {
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    let mps = Cc["@mozilla.org/gecko-media-plugin-service;1"].getService(
 | 
						|
      Ci.mozIGeckoMediaPluginChromeService
 | 
						|
    );
 | 
						|
    mps.forgetThisSite(aHost, JSON.stringify(aOriginAttributes));
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    return this.deleteByHost(aPrincipal.host, aPrincipal.originAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain) {
 | 
						|
    let mps = Cc["@mozilla.org/gecko-media-plugin-service;1"].getService(
 | 
						|
      Ci.mozIGeckoMediaPluginChromeService
 | 
						|
    );
 | 
						|
    mps.forgetThisBaseDomain(aBaseDomain);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    // Not implemented.
 | 
						|
    return Promise.resolve();
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const ReportsCleaner = {
 | 
						|
  deleteByHost(aHost) {
 | 
						|
    // Also clears subdomains of aHost.
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      Services.obs.notifyObservers(null, "reporting:purge-host", aHost);
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByPrincipal(aPrincipal) {
 | 
						|
    return this.deleteByHost(aPrincipal.host, aPrincipal.originAttributes);
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByBaseDomain(aBaseDomain) {
 | 
						|
    return this.deleteByHost(aBaseDomain, {});
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    return new Promise(aResolve => {
 | 
						|
      Services.obs.notifyObservers(null, "reporting:purge-all");
 | 
						|
      aResolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const ContentBlockingCleaner = {
 | 
						|
  deleteAll() {
 | 
						|
    return lazy.TrackingDBService.clearAll();
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByPrincipal(aPrincipal, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  deleteByRange(aFrom) {
 | 
						|
    return lazy.TrackingDBService.clearSince(aFrom);
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * The about:home startup cache, if it exists, might contain information
 | 
						|
 * about where the user has been, or what they've downloaded.
 | 
						|
 */
 | 
						|
const AboutHomeStartupCacheCleaner = {
 | 
						|
  async deleteByPrincipal(aPrincipal, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  deleteAll() {
 | 
						|
    // This cleaner only makes sense on Firefox desktop, which is the only
 | 
						|
    // application that uses the about:home startup cache.
 | 
						|
    if (!AppConstants.MOZ_BUILD_APP == "browser") {
 | 
						|
      return Promise.resolve();
 | 
						|
    }
 | 
						|
 | 
						|
    return new Promise((aResolve, aReject) => {
 | 
						|
      let lci = Services.loadContextInfo.default;
 | 
						|
      let storage = Services.cache2.diskCacheStorage(lci);
 | 
						|
      let uri = Services.io.newURI("about:home");
 | 
						|
      try {
 | 
						|
        storage.asyncDoomURI(uri, "", {
 | 
						|
          onCacheEntryDoomed(aResult) {
 | 
						|
            if (
 | 
						|
              Components.isSuccessCode(aResult) ||
 | 
						|
              aResult == Cr.NS_ERROR_NOT_AVAILABLE
 | 
						|
            ) {
 | 
						|
              aResolve();
 | 
						|
            } else {
 | 
						|
              aReject({
 | 
						|
                message: "asyncDoomURI for about:home failed",
 | 
						|
              });
 | 
						|
            }
 | 
						|
          },
 | 
						|
        });
 | 
						|
      } catch (e) {
 | 
						|
        aReject({
 | 
						|
          message: "Failed to doom about:home startup cache entry",
 | 
						|
        });
 | 
						|
      }
 | 
						|
    });
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const PreflightCacheCleaner = {
 | 
						|
  // TODO: Bug 1727141: We should call the cache to clear by principal, rather
 | 
						|
  // than over-clearing for user requests or bailing out for programmatic calls.
 | 
						|
  async deleteByPrincipal(aPrincipal, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  // TODO: Bug 1727141 (see deleteByPrincipal).
 | 
						|
  async deleteByBaseDomain(aBaseDomain, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await this.deleteAll();
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    Cc[`@mozilla.org/network/protocol;1?name=http`]
 | 
						|
      .getService(Ci.nsIHttpProtocolHandler)
 | 
						|
      .clearCORSPreflightCache();
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const IdentityCredentialStorageCleaner = {
 | 
						|
  async deleteAll() {
 | 
						|
    if (
 | 
						|
      Services.prefs.getBoolPref(
 | 
						|
        "dom.security.credentialmanagement.identity.enabled",
 | 
						|
        false
 | 
						|
      )
 | 
						|
    ) {
 | 
						|
      lazy.IdentityCredentialStorageService.clear();
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByPrincipal(aPrincipal) {
 | 
						|
    if (
 | 
						|
      Services.prefs.getBoolPref(
 | 
						|
        "dom.security.credentialmanagement.identity.enabled",
 | 
						|
        false
 | 
						|
      )
 | 
						|
    ) {
 | 
						|
      lazy.IdentityCredentialStorageService.deleteFromPrincipal(aPrincipal);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain, aIsUserRequest) {
 | 
						|
    if (!aIsUserRequest) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (
 | 
						|
      Services.prefs.getBoolPref(
 | 
						|
        "dom.security.credentialmanagement.identity.enabled",
 | 
						|
        false
 | 
						|
      )
 | 
						|
    ) {
 | 
						|
      lazy.IdentityCredentialStorageService.deleteFromBaseDomain(aBaseDomain);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByRange(aFrom, aTo) {
 | 
						|
    if (
 | 
						|
      Services.prefs.getBoolPref(
 | 
						|
        "dom.security.credentialmanagement.identity.enabled",
 | 
						|
        false
 | 
						|
      )
 | 
						|
    ) {
 | 
						|
      lazy.IdentityCredentialStorageService.deleteFromTimeRange(aFrom, aTo);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByHost(aHost, aOriginAttributes) {
 | 
						|
    if (
 | 
						|
      Services.prefs.getBoolPref(
 | 
						|
        "dom.security.credentialmanagement.identity.enabled",
 | 
						|
        false
 | 
						|
      )
 | 
						|
    ) {
 | 
						|
      // Delete data from both HTTP and HTTPS sites.
 | 
						|
      let httpURI = Services.io.newURI("http://" + aHost);
 | 
						|
      let httpsURI = Services.io.newURI("https://" + aHost);
 | 
						|
      let httpPrincipal = Services.scriptSecurityManager.createContentPrincipal(
 | 
						|
        httpURI,
 | 
						|
        aOriginAttributes
 | 
						|
      );
 | 
						|
      let httpsPrincipal =
 | 
						|
        Services.scriptSecurityManager.createContentPrincipal(
 | 
						|
          httpsURI,
 | 
						|
          aOriginAttributes
 | 
						|
        );
 | 
						|
      lazy.IdentityCredentialStorageService.deleteFromPrincipal(httpPrincipal);
 | 
						|
      lazy.IdentityCredentialStorageService.deleteFromPrincipal(httpsPrincipal);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByOriginAttributes(aOriginAttributesString) {
 | 
						|
    if (
 | 
						|
      Services.prefs.getBoolPref(
 | 
						|
        "dom.security.credentialmanagement.identity.enabled",
 | 
						|
        false
 | 
						|
      )
 | 
						|
    ) {
 | 
						|
      lazy.IdentityCredentialStorageService.deleteFromOriginAttributesPattern(
 | 
						|
        aOriginAttributesString
 | 
						|
      );
 | 
						|
    }
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const BounceTrackingProtectionStateCleaner = {
 | 
						|
  async deleteAll() {
 | 
						|
    if (!lazy.isBounceTrackingProtectionEnabled) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await lazy.bounceTrackingProtection.clearAll();
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByPrincipal(aPrincipal) {
 | 
						|
    if (!lazy.isBounceTrackingProtectionEnabled) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    let { baseDomain, originAttributes } = aPrincipal;
 | 
						|
    await lazy.bounceTrackingProtection.clearBySiteHostAndOA(
 | 
						|
      baseDomain,
 | 
						|
      originAttributes
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain) {
 | 
						|
    if (!lazy.isBounceTrackingProtectionEnabled) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await lazy.bounceTrackingProtection.clearBySiteHost(aBaseDomain);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByRange(aFrom, aTo) {
 | 
						|
    if (!lazy.isBounceTrackingProtectionEnabled) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await lazy.bounceTrackingProtection.clearByTimeRange(aFrom, aTo);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByHost(aHost) {
 | 
						|
    if (!lazy.isBounceTrackingProtectionEnabled) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    let baseDomain = getBaseDomainWithFallback(aHost);
 | 
						|
    await lazy.bounceTrackingProtection.clearBySiteHost(baseDomain);
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByOriginAttributes(aOriginAttributesPatternString) {
 | 
						|
    if (!lazy.isBounceTrackingProtectionEnabled) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    await lazy.bounceTrackingProtection.clearByOriginAttributesPattern(
 | 
						|
      aOriginAttributesPatternString
 | 
						|
    );
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
const StoragePermissionsCleaner = {
 | 
						|
  async deleteByRange(aFrom) {
 | 
						|
    // We lack the ability to clear by range, but can clear from a certain time to now
 | 
						|
    // Convert aFrom from microseconds to ms
 | 
						|
    Services.perms.removeByTypeSince("storage-access", aFrom / 1000);
 | 
						|
 | 
						|
    let persistentStoragePermissions = Services.perms.getAllByTypeSince(
 | 
						|
      "persistent-storage",
 | 
						|
      aFrom / 1000
 | 
						|
    );
 | 
						|
    persistentStoragePermissions.forEach(perm => {
 | 
						|
      // If it is an Addon Principal, do nothing.
 | 
						|
      // We want their persistant-storage permissions to remain (Bug 1907732)
 | 
						|
      if (this._isAddonPrincipal(perm.principal)) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      Services.perms.removePermission(perm);
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByPrincipal(aPrincipal) {
 | 
						|
    Services.perms.removeFromPrincipal(aPrincipal, "storage-access");
 | 
						|
 | 
						|
    // Only remove persistent-storage if it is not an extension principal (Bug 1907732)
 | 
						|
    if (!this._isAddonPrincipal(aPrincipal)) {
 | 
						|
      Services.perms.removeFromPrincipal(aPrincipal, "persistent-storage");
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByHost(aHost) {
 | 
						|
    let permissions = this._getStoragePermissions();
 | 
						|
    for (let perm of permissions) {
 | 
						|
      if (Services.eTLD.hasRootDomain(perm.principal.host, aHost)) {
 | 
						|
        Services.perms.removePermission(perm);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByBaseDomain(aBaseDomain) {
 | 
						|
    let permissions = this._getStoragePermissions();
 | 
						|
    for (let perm of permissions) {
 | 
						|
      if (perm.principal.baseDomain == aBaseDomain) {
 | 
						|
        Services.perms.removePermission(perm);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteByLocalFiles() {
 | 
						|
    let permissions = this._getStoragePermissions();
 | 
						|
    for (let perm of permissions) {
 | 
						|
      if (perm.principal.schemeIs("file")) {
 | 
						|
        Services.perms.removePermission(perm);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  async deleteAll() {
 | 
						|
    Services.perms.removeByType("storage-access");
 | 
						|
 | 
						|
    // We don't want to clear the persistent-storage permission from addons (Bug 1907732)
 | 
						|
    let persistentStoragePermissions = Services.perms.getAllByTypes([
 | 
						|
      "persistent-storage",
 | 
						|
    ]);
 | 
						|
    persistentStoragePermissions.forEach(perm => {
 | 
						|
      if (this._isAddonPrincipal(perm.principal)) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      Services.perms.removePermission(perm);
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  _getStoragePermissions() {
 | 
						|
    let storagePermissions = Services.perms.getAllByTypes([
 | 
						|
      "storage-access",
 | 
						|
      "persistent-storage",
 | 
						|
    ]);
 | 
						|
 | 
						|
    return storagePermissions.filter(
 | 
						|
      permission =>
 | 
						|
        !this._isAddonPrincipal(permission.principal) ||
 | 
						|
        permission.type == "storage-access"
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  _isAddonPrincipal(aPrincipal) {
 | 
						|
    return (
 | 
						|
      // AddonPolicy() returns a WebExtensionPolicy that has been registered before,
 | 
						|
      // typically during extension startup. Since Disabled or uninstalled add-ons
 | 
						|
      // don't appear there, we should use schemeIs instead
 | 
						|
      aPrincipal.schemeIs("moz-extension")
 | 
						|
    );
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
// Here the map of Flags-Cleaners.
 | 
						|
const FLAGS_MAP = [
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_CERT_EXCEPTIONS,
 | 
						|
    cleaners: [CertCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  { flag: Ci.nsIClearDataService.CLEAR_COOKIES, cleaners: [CookieCleaner] },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_NETWORK_CACHE,
 | 
						|
    cleaners: [NetworkCacheCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_IMAGE_CACHE,
 | 
						|
    cleaners: [ImageCacheCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_CSS_CACHE,
 | 
						|
    cleaners: [CSSCacheCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_CLIENT_AUTH_REMEMBER_SERVICE,
 | 
						|
    cleaners: [ClientAuthRememberCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_DOWNLOADS,
 | 
						|
    cleaners: [DownloadsCleaner, AboutHomeStartupCacheCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_PASSWORDS,
 | 
						|
    cleaners: [PasswordsCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_MEDIA_DEVICES,
 | 
						|
    cleaners: [MediaDevicesCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  { flag: Ci.nsIClearDataService.CLEAR_DOM_QUOTA, cleaners: [QuotaCleaner] },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_PREDICTOR_NETWORK_DATA,
 | 
						|
    cleaners: [PredictorNetworkCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_DOM_PUSH_NOTIFICATIONS,
 | 
						|
    cleaners: [PushNotificationsCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_HISTORY,
 | 
						|
    cleaners: [HistoryCleaner, AboutHomeStartupCacheCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_SESSION_HISTORY,
 | 
						|
    cleaners: [SessionHistoryCleaner, AboutHomeStartupCacheCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_AUTH_TOKENS,
 | 
						|
    cleaners: [AuthTokensCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_AUTH_CACHE,
 | 
						|
    cleaners: [AuthCacheCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_PERMISSIONS,
 | 
						|
    cleaners: [PermissionsCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_CONTENT_PREFERENCES,
 | 
						|
    cleaners: [PreferencesCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_HSTS,
 | 
						|
    cleaners: [HSTSCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  { flag: Ci.nsIClearDataService.CLEAR_EME, cleaners: [EMECleaner] },
 | 
						|
 | 
						|
  { flag: Ci.nsIClearDataService.CLEAR_REPORTS, cleaners: [ReportsCleaner] },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_STORAGE_ACCESS,
 | 
						|
    cleaners: [StorageAccessCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_CONTENT_BLOCKING_RECORDS,
 | 
						|
    cleaners: [ContentBlockingCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_PREFLIGHT_CACHE,
 | 
						|
    cleaners: [PreflightCacheCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_CREDENTIAL_MANAGER_STATE,
 | 
						|
    cleaners: [IdentityCredentialStorageCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_COOKIE_BANNER_EXCEPTION,
 | 
						|
    cleaners: [CookieBannerExceptionCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_COOKIE_BANNER_EXECUTED_RECORD,
 | 
						|
    cleaners: [CookieBannerExecutedRecordCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_FINGERPRINTING_PROTECTION_STATE,
 | 
						|
    cleaners: [FingerprintingProtectionStateCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_BOUNCE_TRACKING_PROTECTION_STATE,
 | 
						|
    cleaners: [BounceTrackingProtectionStateCleaner],
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    flag: Ci.nsIClearDataService.CLEAR_STORAGE_PERMISSIONS,
 | 
						|
    cleaners: [StoragePermissionsCleaner],
 | 
						|
  },
 | 
						|
];
 | 
						|
 | 
						|
export function ClearDataService() {
 | 
						|
  this._initialize();
 | 
						|
}
 | 
						|
 | 
						|
ClearDataService.prototype = Object.freeze({
 | 
						|
  classID: Components.ID("{0c06583d-7dd8-4293-b1a5-912205f779aa}"),
 | 
						|
  QueryInterface: ChromeUtils.generateQI(["nsIClearDataService"]),
 | 
						|
 | 
						|
  _initialize() {
 | 
						|
    // Let's start all the service we need to cleanup data.
 | 
						|
 | 
						|
    // This is mainly needed for GeckoView that doesn't start QMS on startup
 | 
						|
    // time.
 | 
						|
    if (!Services.qms) {
 | 
						|
      console.error("Failed initializiation of QuotaManagerService.");
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  deleteDataFromLocalFiles(aIsUserRequest, aFlags, aCallback) {
 | 
						|
    if (!aCallback) {
 | 
						|
      return Cr.NS_ERROR_INVALID_ARG;
 | 
						|
    }
 | 
						|
 | 
						|
    return this._deleteInternal(aFlags, aCallback, aCleaner => {
 | 
						|
      // Some of the 'Cleaners' do not support clearing data for
 | 
						|
      // local files. Ignore those.
 | 
						|
      if (aCleaner.deleteByLocalFiles) {
 | 
						|
        // A generic originAttributes dictionary.
 | 
						|
        return aCleaner.deleteByLocalFiles({});
 | 
						|
      }
 | 
						|
      return Promise.resolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteDataFromHost(aHost, aIsUserRequest, aFlags, aCallback) {
 | 
						|
    if (!aHost || !aCallback) {
 | 
						|
      return Cr.NS_ERROR_INVALID_ARG;
 | 
						|
    }
 | 
						|
 | 
						|
    return this._deleteInternal(aFlags, aCallback, aCleaner => {
 | 
						|
      // Some of the 'Cleaners' do not support to delete by principal. Let's
 | 
						|
      // use deleteAll() as fallback.
 | 
						|
      if (aCleaner.deleteByHost) {
 | 
						|
        // A generic originAttributes dictionary.
 | 
						|
        return aCleaner.deleteByHost(aHost, {});
 | 
						|
      }
 | 
						|
      // The user wants to delete data. Let's remove as much as we can.
 | 
						|
      if (aIsUserRequest) {
 | 
						|
        return aCleaner.deleteAll();
 | 
						|
      }
 | 
						|
      // We don't want to delete more than what is strictly required.
 | 
						|
      return Promise.resolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteDataFromBaseDomain(aDomainOrHost, aIsUserRequest, aFlags, aCallback) {
 | 
						|
    if (!aDomainOrHost || !aCallback) {
 | 
						|
      return Cr.NS_ERROR_INVALID_ARG;
 | 
						|
    }
 | 
						|
    // We may throw here if aDomainOrHost can't be converted to a base domain.
 | 
						|
    let baseDomain;
 | 
						|
 | 
						|
    try {
 | 
						|
      baseDomain = getBaseDomainWithFallback(aDomainOrHost);
 | 
						|
    } catch (e) {
 | 
						|
      return Cr.NS_ERROR_FAILURE;
 | 
						|
    }
 | 
						|
 | 
						|
    return this._deleteInternal(aFlags, aCallback, aCleaner =>
 | 
						|
      aCleaner.deleteByBaseDomain(baseDomain, aIsUserRequest)
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  deleteDataFromPrincipal(aPrincipal, aIsUserRequest, aFlags, aCallback) {
 | 
						|
    if (!aPrincipal || !aCallback) {
 | 
						|
      return Cr.NS_ERROR_INVALID_ARG;
 | 
						|
    }
 | 
						|
 | 
						|
    return this._deleteInternal(aFlags, aCallback, aCleaner =>
 | 
						|
      aCleaner.deleteByPrincipal(aPrincipal, aIsUserRequest)
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  deleteDataInTimeRange(aFrom, aTo, aIsUserRequest, aFlags, aCallback) {
 | 
						|
    if (aFrom > aTo || !aCallback) {
 | 
						|
      return Cr.NS_ERROR_INVALID_ARG;
 | 
						|
    }
 | 
						|
 | 
						|
    return this._deleteInternal(aFlags, aCallback, aCleaner => {
 | 
						|
      // Some of the 'Cleaners' do not support to delete by range. Let's use
 | 
						|
      // deleteAll() as fallback.
 | 
						|
      if (aCleaner.deleteByRange) {
 | 
						|
        return aCleaner.deleteByRange(aFrom, aTo);
 | 
						|
      }
 | 
						|
      // The user wants to delete data. Let's remove as much as we can.
 | 
						|
      if (aIsUserRequest) {
 | 
						|
        return aCleaner.deleteAll();
 | 
						|
      }
 | 
						|
      // We don't want to delete more than what is strictly required.
 | 
						|
      return Promise.resolve();
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteData(aFlags, aCallback) {
 | 
						|
    if (!aCallback) {
 | 
						|
      return Cr.NS_ERROR_INVALID_ARG;
 | 
						|
    }
 | 
						|
 | 
						|
    return this._deleteInternal(aFlags, aCallback, aCleaner => {
 | 
						|
      return aCleaner.deleteAll();
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  deleteDataFromOriginAttributesPattern(aPattern, aCallback) {
 | 
						|
    if (!aPattern) {
 | 
						|
      return Cr.NS_ERROR_INVALID_ARG;
 | 
						|
    }
 | 
						|
 | 
						|
    let patternString = JSON.stringify(aPattern);
 | 
						|
    // XXXtt remove clear-origin-attributes-data entirely
 | 
						|
    Services.obs.notifyObservers(
 | 
						|
      null,
 | 
						|
      "clear-origin-attributes-data",
 | 
						|
      patternString
 | 
						|
    );
 | 
						|
 | 
						|
    if (!aCallback) {
 | 
						|
      aCallback = {
 | 
						|
        onDataDeleted: () => {},
 | 
						|
      };
 | 
						|
    }
 | 
						|
    return this._deleteInternal(
 | 
						|
      Ci.nsIClearDataService.CLEAR_ALL,
 | 
						|
      aCallback,
 | 
						|
      aCleaner => {
 | 
						|
        if (aCleaner.deleteByOriginAttributes) {
 | 
						|
          return aCleaner.deleteByOriginAttributes(patternString);
 | 
						|
        }
 | 
						|
 | 
						|
        // We don't want to delete more than what is strictly required.
 | 
						|
        return Promise.resolve();
 | 
						|
      }
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  deleteUserInteractionForClearingHistory(
 | 
						|
    aPrincipalsWithStorage,
 | 
						|
    aFrom,
 | 
						|
    aCallback
 | 
						|
  ) {
 | 
						|
    if (!aCallback) {
 | 
						|
      return Cr.NS_ERROR_INVALID_ARG;
 | 
						|
    }
 | 
						|
 | 
						|
    StorageAccessCleaner.deleteExceptPrincipals(aPrincipalsWithStorage, aFrom)
 | 
						|
      .then(() => {
 | 
						|
        aCallback.onDataDeleted(0);
 | 
						|
      })
 | 
						|
      .catch(() => {
 | 
						|
        // This is part of clearing storageAccessAPI permissions, thus return
 | 
						|
        // an appropriate error flag.
 | 
						|
        aCallback.onDataDeleted(Ci.nsIClearDataService.CLEAR_PERMISSIONS);
 | 
						|
      });
 | 
						|
    return Cr.NS_OK;
 | 
						|
  },
 | 
						|
 | 
						|
  cleanupAfterDeletionAtShutdown(aFlags, aCallback) {
 | 
						|
    return this._deleteInternal(aFlags, aCallback, async aCleaner => {
 | 
						|
      if (aCleaner.cleanupAfterDeletionAtShutdown) {
 | 
						|
        await aCleaner.cleanupAfterDeletionAtShutdown();
 | 
						|
      }
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  // This internal method uses aFlags against FLAGS_MAP in order to retrieve a
 | 
						|
  // list of 'Cleaners'. For each of them, the aHelper callback retrieves a
 | 
						|
  // promise object. All these promise objects are resolved before calling
 | 
						|
  // onDataDeleted.
 | 
						|
  _deleteInternal(aFlags, aCallback, aHelper) {
 | 
						|
    let resultFlags = 0;
 | 
						|
    let promises = FLAGS_MAP.filter(c => aFlags & c.flag).map(c => {
 | 
						|
      return Promise.all(
 | 
						|
        c.cleaners.map(cleaner => {
 | 
						|
          return aHelper(cleaner).catch(e => {
 | 
						|
            console.error(e);
 | 
						|
            resultFlags |= c.flag;
 | 
						|
          });
 | 
						|
        })
 | 
						|
      );
 | 
						|
      // Let's collect the failure in resultFlags.
 | 
						|
    });
 | 
						|
    Promise.all(promises).then(() => {
 | 
						|
      aCallback.onDataDeleted(resultFlags);
 | 
						|
    });
 | 
						|
    return Cr.NS_OK;
 | 
						|
  },
 | 
						|
});
 |