tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

api.js (5943B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 /* globals ExtensionAPI, Services, XPCOMUtils */
      8 
      9 const CACHED_STYLESHEETS = new WeakMap();
     10 
     11 ChromeUtils.defineESModuleGetters(this, {
     12  FormAutofill: "resource://autofill/FormAutofill.sys.mjs",
     13  FormAutofillParent: "resource://autofill/FormAutofillParent.sys.mjs",
     14  FormAutofillStatus: "resource://autofill/FormAutofillParent.sys.mjs",
     15  AutoCompleteParent: "resource://gre/actors/AutoCompleteParent.sys.mjs",
     16 });
     17 
     18 XPCOMUtils.defineLazyServiceGetter(
     19  this,
     20  "resProto",
     21  "@mozilla.org/network/protocol;1?name=resource",
     22  Ci.nsISubstitutingProtocolHandler
     23 );
     24 
     25 const RESOURCE_HOST = "formautofill";
     26 
     27 function insertStyleSheet(domWindow, url) {
     28  let doc = domWindow.document;
     29  let styleSheetAttr = `href="${url}" type="text/css"`;
     30  let styleSheet = doc.createProcessingInstruction(
     31    "xml-stylesheet",
     32    styleSheetAttr
     33  );
     34 
     35  doc.insertBefore(styleSheet, doc.documentElement);
     36 
     37  if (CACHED_STYLESHEETS.has(domWindow)) {
     38    CACHED_STYLESHEETS.get(domWindow).push(styleSheet);
     39  } else {
     40    CACHED_STYLESHEETS.set(domWindow, [styleSheet]);
     41  }
     42 }
     43 
     44 function ensureCssLoaded(domWindow) {
     45  if (CACHED_STYLESHEETS.has(domWindow)) {
     46    // This window already has autofill stylesheets.
     47    return;
     48  }
     49 
     50  insertStyleSheet(domWindow, "chrome://formautofill/content/formautofill.css");
     51 }
     52 
     53 this.formautofill = class extends ExtensionAPI {
     54  /**
     55   * Adjusts and checks form autofill preferences during startup.
     56   *
     57   * @param {boolean} addressAutofillAvailable
     58   * @param {boolean} creditCardAutofillAvailable
     59   */
     60  adjustAndCheckFormAutofillPrefs(
     61    addressAutofillAvailable,
     62    creditCardAutofillAvailable
     63  ) {
     64    // Reset the sync prefs in case the features were previously available
     65    // but aren't now.
     66    if (!creditCardAutofillAvailable) {
     67      Services.prefs.clearUserPref(
     68        "services.sync.engine.creditcards.available"
     69      );
     70    }
     71    if (!addressAutofillAvailable) {
     72      Services.prefs.clearUserPref("services.sync.engine.addresses.available");
     73    }
     74 
     75    if (!addressAutofillAvailable && !creditCardAutofillAvailable) {
     76      Services.prefs.clearUserPref("dom.forms.autocomplete.formautofill");
     77      Glean.formautofill.availability.set(false);
     78      return;
     79    }
     80 
     81    // This pref is used for web contents to detect the autocomplete feature.
     82    // When it's true, "element.autocomplete" will return tokens we currently
     83    // support -- otherwise it'll return an empty string.
     84    Services.prefs.setBoolPref("dom.forms.autocomplete.formautofill", true);
     85    Glean.formautofill.availability.set(true);
     86 
     87    // These "*.available" prefs determines whether the "addresses"/"creditcards" sync engine is
     88    // available (ie, whether it is shown in any UI etc) - it *does not* determine
     89    // whether the engine is actually enabled or not.
     90    if (FormAutofill.isAutofillAddressesAvailable) {
     91      Services.prefs.setBoolPref(
     92        "services.sync.engine.addresses.available",
     93        true
     94      );
     95    } else {
     96      Services.prefs.clearUserPref("services.sync.engine.addresses.available");
     97    }
     98    if (FormAutofill.isAutofillCreditCardsAvailable) {
     99      Services.prefs.setBoolPref(
    100        "services.sync.engine.creditcards.available",
    101        true
    102      );
    103    } else {
    104      Services.prefs.clearUserPref(
    105        "services.sync.engine.creditcards.available"
    106      );
    107    }
    108  }
    109  onStartup() {
    110    // We have to do this before actually determining if we're enabled, since
    111    // there are scripts inside of the core browser code that depend on the
    112    // FormAutofill JSMs being registered.
    113    let uri = Services.io.newURI("chrome/res/", null, this.extension.rootURI);
    114    resProto.setSubstitution(RESOURCE_HOST, uri);
    115 
    116    let aomStartup = Cc[
    117      "@mozilla.org/addons/addon-manager-startup;1"
    118    ].getService(Ci.amIAddonManagerStartup);
    119    const manifestURI = Services.io.newURI(
    120      "manifest.json",
    121      null,
    122      this.extension.rootURI
    123    );
    124    this.chromeHandle = aomStartup.registerChrome(manifestURI, [
    125      ["content", "formautofill", "content/"],
    126    ]);
    127 
    128    this.adjustAndCheckFormAutofillPrefs(
    129      FormAutofill.isAutofillAddressesAvailable,
    130      FormAutofill.isAutofillCreditCardsAvailable
    131    );
    132 
    133    // Listen for the autocomplete popup message
    134    // or the form submitted message (which may trigger a
    135    // doorhanger) to lazily append our stylesheets related
    136    // to the autocomplete feature.
    137    AutoCompleteParent.addPopupStateListener(ensureCssLoaded);
    138    FormAutofillParent.addMessageObserver(this);
    139    this.onFormSubmitted = (data, window) => ensureCssLoaded(window);
    140 
    141    FormAutofillStatus.init();
    142 
    143    ChromeUtils.registerWindowActor("FormAutofill", {
    144      parent: {
    145        esModuleURI: "resource://autofill/FormAutofillParent.sys.mjs",
    146      },
    147      child: {
    148        esModuleURI: "resource://autofill/FormAutofillChild.sys.mjs",
    149        events: {
    150          focusin: { capture: true },
    151          "form-changed": { createActor: false },
    152          "form-submission-detected": { createActor: false },
    153        },
    154      },
    155      allFrames: true,
    156    });
    157  }
    158 
    159  onShutdown(isAppShutdown) {
    160    if (isAppShutdown) {
    161      return;
    162    }
    163 
    164    resProto.setSubstitution(RESOURCE_HOST, null);
    165 
    166    this.chromeHandle.destruct();
    167    this.chromeHandle = null;
    168 
    169    ChromeUtils.unregisterWindowActor("FormAutofill");
    170 
    171    AutoCompleteParent.removePopupStateListener(ensureCssLoaded);
    172    FormAutofillParent.removeMessageObserver(this);
    173 
    174    for (let win of Services.wm.getEnumerator("navigator:browser")) {
    175      let cachedStyleSheets = CACHED_STYLESHEETS.get(win);
    176 
    177      if (!cachedStyleSheets) {
    178        continue;
    179      }
    180 
    181      while (cachedStyleSheets.length !== 0) {
    182        cachedStyleSheets.pop().remove();
    183      }
    184    }
    185  }
    186 };