mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			186 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			186 lines
		
	
	
	
		
			5.8 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/. */
 | 
						|
 | 
						|
"use strict";
 | 
						|
 | 
						|
/* globals ExtensionAPI, Services, XPCOMUtils */
 | 
						|
 | 
						|
const CACHED_STYLESHEETS = new WeakMap();
 | 
						|
 | 
						|
ChromeUtils.defineESModuleGetters(this, {
 | 
						|
  FormAutofill: "resource://autofill/FormAutofill.sys.mjs",
 | 
						|
  FormAutofillParent: "resource://autofill/FormAutofillParent.sys.mjs",
 | 
						|
  FormAutofillStatus: "resource://autofill/FormAutofillParent.sys.mjs",
 | 
						|
  AutoCompleteParent: "resource://gre/actors/AutoCompleteParent.sys.mjs",
 | 
						|
});
 | 
						|
 | 
						|
XPCOMUtils.defineLazyServiceGetter(
 | 
						|
  this,
 | 
						|
  "resProto",
 | 
						|
  "@mozilla.org/network/protocol;1?name=resource",
 | 
						|
  "nsISubstitutingProtocolHandler"
 | 
						|
);
 | 
						|
 | 
						|
const RESOURCE_HOST = "formautofill";
 | 
						|
 | 
						|
function insertStyleSheet(domWindow, url) {
 | 
						|
  let doc = domWindow.document;
 | 
						|
  let styleSheetAttr = `href="${url}" type="text/css"`;
 | 
						|
  let styleSheet = doc.createProcessingInstruction(
 | 
						|
    "xml-stylesheet",
 | 
						|
    styleSheetAttr
 | 
						|
  );
 | 
						|
 | 
						|
  doc.insertBefore(styleSheet, doc.documentElement);
 | 
						|
 | 
						|
  if (CACHED_STYLESHEETS.has(domWindow)) {
 | 
						|
    CACHED_STYLESHEETS.get(domWindow).push(styleSheet);
 | 
						|
  } else {
 | 
						|
    CACHED_STYLESHEETS.set(domWindow, [styleSheet]);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function ensureCssLoaded(domWindow) {
 | 
						|
  if (CACHED_STYLESHEETS.has(domWindow)) {
 | 
						|
    // This window already has autofill stylesheets.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  insertStyleSheet(domWindow, "chrome://formautofill/content/formautofill.css");
 | 
						|
}
 | 
						|
 | 
						|
this.formautofill = class extends ExtensionAPI {
 | 
						|
  /**
 | 
						|
   * Adjusts and checks form autofill preferences during startup.
 | 
						|
   *
 | 
						|
   * @param {boolean} addressAutofillAvailable
 | 
						|
   * @param {boolean} creditCardAutofillAvailable
 | 
						|
   */
 | 
						|
  adjustAndCheckFormAutofillPrefs(
 | 
						|
    addressAutofillAvailable,
 | 
						|
    creditCardAutofillAvailable
 | 
						|
  ) {
 | 
						|
    // Reset the sync prefs in case the features were previously available
 | 
						|
    // but aren't now.
 | 
						|
    if (!creditCardAutofillAvailable) {
 | 
						|
      Services.prefs.clearUserPref(
 | 
						|
        "services.sync.engine.creditcards.available"
 | 
						|
      );
 | 
						|
    }
 | 
						|
    if (!addressAutofillAvailable) {
 | 
						|
      Services.prefs.clearUserPref("services.sync.engine.addresses.available");
 | 
						|
    }
 | 
						|
 | 
						|
    if (!addressAutofillAvailable && !creditCardAutofillAvailable) {
 | 
						|
      Services.prefs.clearUserPref("dom.forms.autocomplete.formautofill");
 | 
						|
      Glean.formautofill.availability.set(false);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // This pref is used for web contents to detect the autocomplete feature.
 | 
						|
    // When it's true, "element.autocomplete" will return tokens we currently
 | 
						|
    // support -- otherwise it'll return an empty string.
 | 
						|
    Services.prefs.setBoolPref("dom.forms.autocomplete.formautofill", true);
 | 
						|
    Glean.formautofill.availability.set(true);
 | 
						|
 | 
						|
    // These "*.available" prefs determines whether the "addresses"/"creditcards" sync engine is
 | 
						|
    // available (ie, whether it is shown in any UI etc) - it *does not* determine
 | 
						|
    // whether the engine is actually enabled or not.
 | 
						|
    if (FormAutofill.isAutofillAddressesAvailable) {
 | 
						|
      Services.prefs.setBoolPref(
 | 
						|
        "services.sync.engine.addresses.available",
 | 
						|
        true
 | 
						|
      );
 | 
						|
    } else {
 | 
						|
      Services.prefs.clearUserPref("services.sync.engine.addresses.available");
 | 
						|
    }
 | 
						|
    if (FormAutofill.isAutofillCreditCardsAvailable) {
 | 
						|
      Services.prefs.setBoolPref(
 | 
						|
        "services.sync.engine.creditcards.available",
 | 
						|
        true
 | 
						|
      );
 | 
						|
    } else {
 | 
						|
      Services.prefs.clearUserPref(
 | 
						|
        "services.sync.engine.creditcards.available"
 | 
						|
      );
 | 
						|
    }
 | 
						|
  }
 | 
						|
  onStartup() {
 | 
						|
    // We have to do this before actually determining if we're enabled, since
 | 
						|
    // there are scripts inside of the core browser code that depend on the
 | 
						|
    // FormAutofill JSMs being registered.
 | 
						|
    let uri = Services.io.newURI("chrome/res/", null, this.extension.rootURI);
 | 
						|
    resProto.setSubstitution(RESOURCE_HOST, uri);
 | 
						|
 | 
						|
    let aomStartup = Cc[
 | 
						|
      "@mozilla.org/addons/addon-manager-startup;1"
 | 
						|
    ].getService(Ci.amIAddonManagerStartup);
 | 
						|
    const manifestURI = Services.io.newURI(
 | 
						|
      "manifest.json",
 | 
						|
      null,
 | 
						|
      this.extension.rootURI
 | 
						|
    );
 | 
						|
    this.chromeHandle = aomStartup.registerChrome(manifestURI, [
 | 
						|
      ["content", "formautofill", "content/"],
 | 
						|
    ]);
 | 
						|
 | 
						|
    this.adjustAndCheckFormAutofillPrefs(
 | 
						|
      FormAutofill.isAutofillAddressesAvailable,
 | 
						|
      FormAutofill.isAutofillCreditCardsAvailable
 | 
						|
    );
 | 
						|
 | 
						|
    // Listen for the autocomplete popup message
 | 
						|
    // or the form submitted message (which may trigger a
 | 
						|
    // doorhanger) to lazily append our stylesheets related
 | 
						|
    // to the autocomplete feature.
 | 
						|
    AutoCompleteParent.addPopupStateListener(ensureCssLoaded);
 | 
						|
    FormAutofillParent.addMessageObserver(this);
 | 
						|
    this.onFormSubmitted = (data, window) => ensureCssLoaded(window);
 | 
						|
 | 
						|
    FormAutofillStatus.init();
 | 
						|
 | 
						|
    ChromeUtils.registerWindowActor("FormAutofill", {
 | 
						|
      parent: {
 | 
						|
        esModuleURI: "resource://autofill/FormAutofillParent.sys.mjs",
 | 
						|
      },
 | 
						|
      child: {
 | 
						|
        esModuleURI: "resource://autofill/FormAutofillChild.sys.mjs",
 | 
						|
        events: {
 | 
						|
          focusin: {},
 | 
						|
          "form-changed": { createActor: false },
 | 
						|
          "form-submission-detected": { createActor: false },
 | 
						|
        },
 | 
						|
      },
 | 
						|
      allFrames: true,
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  onShutdown(isAppShutdown) {
 | 
						|
    if (isAppShutdown) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    resProto.setSubstitution(RESOURCE_HOST, null);
 | 
						|
 | 
						|
    this.chromeHandle.destruct();
 | 
						|
    this.chromeHandle = null;
 | 
						|
 | 
						|
    ChromeUtils.unregisterWindowActor("FormAutofill");
 | 
						|
 | 
						|
    AutoCompleteParent.removePopupStateListener(ensureCssLoaded);
 | 
						|
    FormAutofillParent.removeMessageObserver(this);
 | 
						|
 | 
						|
    for (let win of Services.wm.getEnumerator("navigator:browser")) {
 | 
						|
      let cachedStyleSheets = CACHED_STYLESHEETS.get(win);
 | 
						|
 | 
						|
      if (!cachedStyleSheets) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      while (cachedStyleSheets.length !== 0) {
 | 
						|
        cachedStyleSheets.pop().remove();
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 |