forked from mirrors/gecko-dev
		
	This ports blocked suggestions to `BaseFeature`. Please see bug 1799264 for details. Depends on D161366 Differential Revision: https://phabricator.services.mozilla.com/D161367
		
			
				
	
	
		
			187 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
	
		
			5.9 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 { BaseFeature } from "resource:///modules/urlbar/private/BaseFeature.sys.mjs";
 | 
						|
 | 
						|
const lazy = {};
 | 
						|
 | 
						|
ChromeUtils.defineESModuleGetters(lazy, {
 | 
						|
  TaskQueue: "resource:///modules/UrlbarUtils.sys.mjs",
 | 
						|
  UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
 | 
						|
});
 | 
						|
 | 
						|
/**
 | 
						|
 * A set of blocked suggestions for quick suggest.
 | 
						|
 */
 | 
						|
export class BlockedSuggestions extends BaseFeature {
 | 
						|
  constructor() {
 | 
						|
    super();
 | 
						|
    this.#taskQueue = new lazy.TaskQueue();
 | 
						|
    lazy.UrlbarPrefs.addObserver(this);
 | 
						|
  }
 | 
						|
 | 
						|
  get shouldEnable() {
 | 
						|
    // Return true so that we'll always load blocked digests when quick suggest
 | 
						|
    // is enabled, even if blocking new suggestions is currently disabled.
 | 
						|
    // Blocking may have been enabled previously, and blocked suggestions should
 | 
						|
    // remain blocked as long as quick suggest as a whole remains enabled.
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  enable(enabled) {
 | 
						|
    if (enabled) {
 | 
						|
      this.#loadDigests();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Blocks a suggestion.
 | 
						|
   *
 | 
						|
   * @param {string} originalUrl
 | 
						|
   *   The suggestion's original URL with its unreplaced timestamp template.
 | 
						|
   */
 | 
						|
  async add(originalUrl) {
 | 
						|
    this.logger.debug(`Queueing add: ${originalUrl}`);
 | 
						|
    await this.#taskQueue.queue(async () => {
 | 
						|
      this.logger.info(`Blocking suggestion: ${originalUrl}`);
 | 
						|
      let digest = await this.#getDigest(originalUrl);
 | 
						|
      this.logger.debug(`Got digest for '${originalUrl}': ${digest}`);
 | 
						|
      this.#digests.add(digest);
 | 
						|
      let json = JSON.stringify([...this.#digests]);
 | 
						|
      this.#updatingDigests = true;
 | 
						|
      try {
 | 
						|
        lazy.UrlbarPrefs.set("quicksuggest.blockedDigests", json);
 | 
						|
      } finally {
 | 
						|
        this.#updatingDigests = false;
 | 
						|
      }
 | 
						|
      this.logger.debug(`All blocked suggestions: ${json}`);
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Gets whether a suggestion is blocked.
 | 
						|
   *
 | 
						|
   * @param {string} originalUrl
 | 
						|
   *   The suggestion's original URL with its unreplaced timestamp template.
 | 
						|
   * @returns {boolean}
 | 
						|
   *   Whether the suggestion is blocked.
 | 
						|
   */
 | 
						|
  async has(originalUrl) {
 | 
						|
    this.logger.debug(`Queueing has: ${originalUrl}`);
 | 
						|
    return this.#taskQueue.queue(async () => {
 | 
						|
      this.logger.info(`Getting blocked status: ${originalUrl}`);
 | 
						|
      let digest = await this.#getDigest(originalUrl);
 | 
						|
      this.logger.debug(`Got digest for '${originalUrl}': ${digest}`);
 | 
						|
      let isBlocked = this.#digests.has(digest);
 | 
						|
      this.logger.info(`Blocked status for '${originalUrl}': ${isBlocked}`);
 | 
						|
      return isBlocked;
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Unblocks all suggestions.
 | 
						|
   */
 | 
						|
  async clear() {
 | 
						|
    this.logger.debug(`Queueing clearBlockedSuggestions`);
 | 
						|
    await this.#taskQueue.queue(() => {
 | 
						|
      this.logger.info(`Clearing all blocked suggestions`);
 | 
						|
      this.#digests.clear();
 | 
						|
      lazy.UrlbarPrefs.clear("quicksuggest.blockedDigests");
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Called when a urlbar pref changes.
 | 
						|
   *
 | 
						|
   * @param {string} pref
 | 
						|
   *   The name of the pref relative to `browser.urlbar`.
 | 
						|
   */
 | 
						|
  onPrefChanged(pref) {
 | 
						|
    switch (pref) {
 | 
						|
      case "quicksuggest.blockedDigests":
 | 
						|
        if (!this.#updatingDigests) {
 | 
						|
          this.logger.info(
 | 
						|
            "browser.urlbar.quicksuggest.blockedDigests changed"
 | 
						|
          );
 | 
						|
          this.#loadDigests();
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Loads blocked suggestion digests from the pref into `#digests`.
 | 
						|
   */
 | 
						|
  async #loadDigests() {
 | 
						|
    this.logger.debug(`Queueing #loadDigests`);
 | 
						|
    await this.#taskQueue.queue(() => {
 | 
						|
      this.logger.info(`Loading blocked suggestion digests`);
 | 
						|
      let json = lazy.UrlbarPrefs.get("quicksuggest.blockedDigests");
 | 
						|
      this.logger.debug(
 | 
						|
        `browser.urlbar.quicksuggest.blockedDigests value: ${json}`
 | 
						|
      );
 | 
						|
      if (!json) {
 | 
						|
        this.logger.info(`There are no blocked suggestion digests`);
 | 
						|
        this.#digests.clear();
 | 
						|
      } else {
 | 
						|
        try {
 | 
						|
          this.#digests = new Set(JSON.parse(json));
 | 
						|
          this.logger.info(`Successfully loaded blocked suggestion digests`);
 | 
						|
        } catch (error) {
 | 
						|
          this.logger.error(
 | 
						|
            `Error loading blocked suggestion digests: ${error}`
 | 
						|
          );
 | 
						|
        }
 | 
						|
      }
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the SHA-1 digest of a string as a 40-character hex-encoded string.
 | 
						|
   *
 | 
						|
   * @param {string} string
 | 
						|
   *   The string to convert to SHA-1
 | 
						|
   * @returns {string}
 | 
						|
   *   The hex-encoded digest of the given string.
 | 
						|
   */
 | 
						|
  async #getDigest(string) {
 | 
						|
    let stringArray = new TextEncoder().encode(string);
 | 
						|
    let hashBuffer = await crypto.subtle.digest("SHA-1", stringArray);
 | 
						|
    let hashArray = new Uint8Array(hashBuffer);
 | 
						|
    return Array.from(hashArray, b => b.toString(16).padStart(2, "0")).join("");
 | 
						|
  }
 | 
						|
 | 
						|
  get _test_readyPromise() {
 | 
						|
    return this.#taskQueue.emptyPromise;
 | 
						|
  }
 | 
						|
 | 
						|
  get _test_digests() {
 | 
						|
    return this.#digests;
 | 
						|
  }
 | 
						|
 | 
						|
  _test_getDigest(string) {
 | 
						|
    return this.#getDigest(string);
 | 
						|
  }
 | 
						|
 | 
						|
  // Set of digests of the original URLs of blocked suggestions. A suggestion's
 | 
						|
  // "original URL" is its URL straight from the source with an unreplaced
 | 
						|
  // timestamp template. For details on the digests, see `#getDigest()`.
 | 
						|
  //
 | 
						|
  // The only reason we use URL digests is that suggestions currently do not
 | 
						|
  // have persistent IDs. We could use the URLs themselves but SHA-1 digests are
 | 
						|
  // only 40 chars long, so they save a little space. This is also consistent
 | 
						|
  // with how blocked tiles on the newtab page are stored, but they use MD5. We
 | 
						|
  // do *not* store digests for any security or obfuscation reason.
 | 
						|
  //
 | 
						|
  // This value is serialized as a JSON'ed array to the
 | 
						|
  // `browser.urlbar.quicksuggest.blockedDigests` pref.
 | 
						|
  #digests = new Set();
 | 
						|
 | 
						|
  // Used to serialize access to blocked suggestions. This is only necessary
 | 
						|
  // because getting a suggestion's URL digest is async.
 | 
						|
  #taskQueue = null;
 | 
						|
 | 
						|
  // Whether blocked digests are currently being updated.
 | 
						|
  #updatingDigests = false;
 | 
						|
}
 |