Backed out 10 changesets (bug 1474143) for perma failing browser_markup_events_01.js CLOSED TREE

Backed out changeset 1b5d04866585 (bug 1474143)
Backed out changeset 78bfb4dd1f6a (bug 1474143)
Backed out changeset 85ec4f1f5f60 (bug 1474143)
Backed out changeset 5c112b77e489 (bug 1474143)
Backed out changeset 5d35599598bb (bug 1474143)
Backed out changeset 9fd0d7a7946f (bug 1474143)
Backed out changeset 1a83be7a75ca (bug 1474143)
Backed out changeset 2fc9b13171d0 (bug 1474143)
Backed out changeset 3983d7b6d9ad (bug 1474143)
Backed out changeset 9fe55dd58cd8 (bug 1474143)

--HG--
rename : browser/actors/FormValidationChild.jsm => browser/actors/FormSubmitChild.jsm
rename : toolkit/components/satchel/FormSubmitChild.jsm => toolkit/components/satchel/formSubmitListener.js
This commit is contained in:
Ciure Andrei 2019-02-22 21:16:49 +02:00
Родитель 78e47bd125
Коммит f7412ac1f2
26 изменённых файлов: 358 добавлений и 327 удалений

Просмотреть файл

@ -3,18 +3,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
/*
* Handles the validation callback from nsIFormFillController and
* the display of the help panel on invalid elements.
*/
var EXPORTED_SYMBOLS = ["FormValidationChild"];
var EXPORTED_SYMBOLS = ["FormSubmitChild"];
const {ActorChild} = ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
const {BrowserUtils} = ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
class FormValidationChild extends ActorChild {
class FormSubmitChild extends ActorChild {
constructor(dispatcher) {
super(dispatcher);

Просмотреть файл

@ -33,7 +33,7 @@ FINAL_TARGET_FILES.actors += [
'ContentSearchChild.jsm',
'ContextMenuChild.jsm',
'DOMFullscreenChild.jsm',
'FormValidationChild.jsm',
'FormSubmitChild.jsm',
'LightweightThemeChild.jsm',
'LightWeightThemeInstallChild.jsm',
'LinkHandlerChild.jsm',

Просмотреть файл

@ -41,12 +41,6 @@ function shouldIgnoreLoginManagerEvent(event) {
return event.target.nodePrincipal.isNullPrincipal;
}
addEventListener("DOMFormBeforeSubmit", function(event) {
if (shouldIgnoreLoginManagerEvent(event)) {
return;
}
LoginManagerContent.onDOMFormBeforeSubmit(event);
});
addEventListener("DOMFormHasPassword", function(event) {
if (shouldIgnoreLoginManagerEvent(event)) {
return;

Просмотреть файл

@ -41,6 +41,10 @@ const whitelist = {
"resource:///modules/sessionstore/ContentSessionStore.jsm",
"resource://gre/modules/sessionstore/SessionHistory.jsm",
// Forms and passwords
"resource://formautofill/FormAutofill.jsm",
"resource://formautofill/FormAutofillContent.jsm",
// Browser front-end
"resource:///actors/AboutReaderChild.jsm",
"resource:///actors/BrowserTabChild.jsm",
@ -84,8 +88,10 @@ const whitelist = {
"chrome://global/content/process-content.js",
"resource:///modules/ContentObservers.js",
"data:,ChromeUtils.import('resource://gre/modules/ExtensionProcessScript.jsm')",
"chrome://satchel/content/formSubmitListener.js",
"resource://devtools/client/jsonview/converter-observer.js",
"resource://gre/modules/WebRequestContent.js",
"data:,new function() {\n ChromeUtils.import(\"resource://formautofill/FormAutofillContent.jsm\");\n }",
]),
};

Просмотреть файл

@ -123,9 +123,9 @@ let ACTORS = {
},
},
FormValidation: {
FormSubmit: {
child: {
module: "resource:///actors/FormValidationChild.jsm",
module: "resource:///actors/FormSubmitChild.jsm",
events: {
"MozInvalidForm": {},
},

Просмотреть файл

@ -243,8 +243,6 @@ let ProfileAutocomplete = {
this._registered = true;
Services.obs.addObserver(this, "autocomplete-will-enter-text");
this.debug("ensureRegistered. Finished with _registered:", this._registered);
},
ensureUnregistered() {
@ -338,6 +336,7 @@ let ProfileAutocomplete = {
* NOTE: Declares it by "var" to make it accessible in unit tests.
*/
var FormAutofillContent = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver]),
/**
* @type {WeakMap} mapping FormLike root HTML elements to FormAutofillHandler objects.
*/
@ -346,9 +345,7 @@ var FormAutofillContent = {
/**
* @type {Set} Set of the fields with usable values in any saved profile.
*/
get savedFieldNames() {
return Services.cpmm.sharedData.get("FormAutofill:savedFieldNames");
},
savedFieldNames: null,
/**
* @type {Object} The object where to store the active items, e.g. element,
@ -358,12 +355,12 @@ var FormAutofillContent = {
init() {
FormAutofill.defineLazyLogGetter(this, "FormAutofillContent");
this.debug("init");
// eslint-disable-next-line mozilla/balanced-listeners
Services.cpmm.sharedData.addEventListener("change", this);
Services.cpmm.addMessageListener("FormAutofill:enabledStatus", this);
Services.cpmm.addMessageListener("FormAutofill:savedFieldNames", this);
Services.obs.addObserver(this, "earlyformsubmit");
let autofillEnabled = Services.cpmm.sharedData.get("FormAutofill:enabled");
let autofillEnabled = Services.cpmm.initialProcessData.autofillEnabled;
// If storage hasn't be initialized yet autofillEnabled is undefined but we need to ensure
// autocomplete is registered before the focusin so register it in this case as long as the
// pref is true.
@ -373,6 +370,9 @@ var FormAutofillContent = {
if (autofillEnabled || shouldEnableAutofill) {
ProfileAutocomplete.ensureRegistered();
}
this.savedFieldNames =
Services.cpmm.initialProcessData.autofillSavedFieldNames;
},
/**
@ -389,55 +389,60 @@ var FormAutofillContent = {
},
/**
* Handle a form submission and early return when:
* Handle earlyformsubmit event and early return when:
* 1. In private browsing mode.
* 2. Could not map any autofill handler by form element.
* 3. Number of filled fields is less than autofill threshold
*
* @param {HTMLElement} formElement Root element which receives submit event.
* @param {Window} domWin Content window only passed for unit tests
* @param {HTMLElement} formElement Root element which receives earlyformsubmit event.
* @param {Object} domWin Content window
* @returns {boolean} Should always return true so form submission isn't canceled.
*/
formSubmitted(formElement, domWin = formElement.ownerGlobal) {
this.debug("Handling form submission");
notify(formElement, domWin) {
try {
this.debug("Notifying form early submission");
if (!FormAutofill.isAutofillEnabled) {
this.debug("Form Autofill is disabled");
return;
if (!FormAutofill.isAutofillEnabled) {
this.debug("Form Autofill is disabled");
return true;
}
if (domWin && PrivateBrowsingUtils.isContentWindowPrivate(domWin)) {
this.debug("Ignoring submission in a private window");
return true;
}
let handler = this._formsDetails.get(formElement);
if (!handler) {
this.debug("Form element could not map to an existing handler");
return true;
}
let records = handler.createRecords();
if (!Object.values(records).some(typeRecords => typeRecords.length)) {
return true;
}
this._onFormSubmit(records, domWin, handler.timeStartedFillingMS);
} catch (ex) {
Cu.reportError(ex);
}
// The `domWin` truthiness test is used by unit tests to bypass this check.
if (domWin && PrivateBrowsingUtils.isContentWindowPrivate(domWin)) {
this.debug("Ignoring submission in a private window");
return;
}
let handler = this._formsDetails.get(formElement);
if (!handler) {
this.debug("Form element could not map to an existing handler");
return;
}
let records = handler.createRecords();
if (!Object.values(records).some(typeRecords => typeRecords.length)) {
return;
}
this._onFormSubmit(records, domWin, handler.timeStartedFillingMS);
return true;
},
handleEvent(evt) {
switch (evt.type) {
case "change": {
if (!evt.changedKeys.includes("FormAutofill:enabled")) {
return;
}
if (Services.cpmm.sharedData.get("FormAutofill:enabled")) {
receiveMessage({name, data}) {
switch (name) {
case "FormAutofill:enabledStatus": {
if (data) {
ProfileAutocomplete.ensureRegistered();
} else {
ProfileAutocomplete.ensureUnregistered();
}
break;
}
case "FormAutofill:savedFieldNames": {
this.savedFieldNames = data;
}
}
},

Просмотреть файл

@ -187,10 +187,10 @@ FormAutofillParent.prototype = {
*/
_onStatusChanged() {
log.debug("_onStatusChanged: Status changed to", this._active);
Services.ppmm.sharedData.set("FormAutofill:enabled", this._active);
// Sync autofill enabled to make sure the value is up-to-date
Services.ppmm.broadcastAsyncMessage("FormAutofill:enabledStatus", this._active);
// Sync process data autofillEnabled to make sure the value up to date
// no matter when the new content process is initialized.
Services.ppmm.sharedData.flush();
Services.ppmm.initialProcessData.autofillEnabled = this._active;
},
/**
@ -200,7 +200,7 @@ FormAutofillParent.prototype = {
* @returns {boolean} whether form autofill is active (enabled and has data)
*/
_computeStatus() {
const savedFieldNames = Services.ppmm.sharedData.get("FormAutofill:savedFieldNames");
const savedFieldNames = Services.ppmm.initialProcessData.autofillSavedFieldNames;
return (Services.prefs.getBoolPref(ENABLED_AUTOFILL_ADDRESSES_PREF) ||
Services.prefs.getBoolPref(ENABLED_AUTOFILL_CREDITCARDS_PREF)) &&
@ -375,20 +375,18 @@ FormAutofillParent.prototype = {
_updateSavedFieldNames() {
log.debug("_updateSavedFieldNames");
let savedFieldNames;
// Don't access the credit cards store unless it is enabled.
if (FormAutofill.isAutofillCreditCardsAvailable) {
savedFieldNames = new Set([
...this.formAutofillStorage.addresses.getSavedFieldNames(),
...this.formAutofillStorage.creditCards.getSavedFieldNames(),
]);
Services.ppmm.initialProcessData.autofillSavedFieldNames =
new Set([...this.formAutofillStorage.addresses.getSavedFieldNames(),
...this.formAutofillStorage.creditCards.getSavedFieldNames()]);
} else {
savedFieldNames = this.formAutofillStorage.addresses.getSavedFieldNames();
Services.ppmm.initialProcessData.autofillSavedFieldNames =
this.formAutofillStorage.addresses.getSavedFieldNames();
}
Services.ppmm.sharedData.set("FormAutofill:savedFieldNames", savedFieldNames);
Services.ppmm.sharedData.flush();
Services.ppmm.broadcastAsyncMessage("FormAutofill:savedFieldNames",
Services.ppmm.initialProcessData.autofillSavedFieldNames);
this._updateStatus();
},

Просмотреть файл

@ -127,6 +127,11 @@ this.formautofill = class extends ExtensionAPI {
Services.mm.addMessageListener("FormAutoComplete:MaybeOpenPopup", onMaybeOpenPopup);
formAutofillParent.init().catch(Cu.reportError);
/* eslint-disable no-unused-vars */
Services.ppmm.loadProcessScript("data:,new " + function() {
ChromeUtils.import("resource://formautofill/FormAutofillContent.jsm");
}, true);
/* eslint-enable no-unused-vars */
Services.mm.loadFrameScript("chrome://formautofill/content/FormAutofillFrameScript.js", true, true);
}

Просмотреть файл

@ -2,7 +2,7 @@
* 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/. */
/**
/*
* Form Autofill frame script.
*/
@ -11,17 +11,18 @@
/* eslint-env mozilla/frame-script */
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
var {FormAutofillContent} = ChromeUtils.import("resource://formautofill/FormAutofillContent.jsm");
ChromeUtils.defineModuleGetter(this, "setTimeout",
"resource://gre/modules/Timer.jsm");
ChromeUtils.defineModuleGetter(this, "FormAutofill",
"resource://formautofill/FormAutofill.jsm");
ChromeUtils.defineModuleGetter(this, "FormAutofillContent",
"resource://formautofill/FormAutofillContent.jsm");
ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
"resource://formautofill/FormAutofillUtils.jsm");
/**
* Handles content's interactions for the frame.
*
* NOTE: Declares it by "var" to make it accessible in unit tests.
*/
var FormAutofillFrameScript = {
_nextHandleElement: null,
@ -48,7 +49,6 @@ var FormAutofillFrameScript = {
init() {
addEventListener("focusin", this);
addEventListener("DOMFormBeforeSubmit", this);
addMessageListener("FormAutofill:PreviewProfile", this);
addMessageListener("FormAutofill:ClearForm", this);
addMessageListener("FormAutoComplete:PopupClosed", this);
@ -59,23 +59,6 @@ var FormAutofillFrameScript = {
if (!evt.isTrusted || !FormAutofill.isAutofillEnabled) {
return;
}
switch (evt.type) {
case "focusin": {
this.onFocusIn(evt);
break;
}
case "DOMFormBeforeSubmit": {
this.onDOMFormBeforeSubmit(evt);
break;
}
default: {
throw new Error("Unexpected event type");
}
}
},
onFocusIn(evt) {
FormAutofillContent.updateActiveInput();
let element = evt.target;
@ -99,20 +82,6 @@ var FormAutofillFrameScript = {
this._doIdentifyAutofillFields();
},
/**
* Handle the DOMFormBeforeSubmit event.
* @param {Event} evt
*/
onDOMFormBeforeSubmit(evt) {
let formElement = evt.target;
if (!FormAutofill.isAutofillEnabled) {
return;
}
FormAutofillContent.formSubmitted(formElement);
},
receiveMessage(message) {
if (!FormAutofill.isAutofillEnabled) {
return;

Просмотреть файл

@ -16,19 +16,19 @@ add_task(async function test_activeStatus_init() {
// Default status is null before initialization
Assert.equal(formAutofillParent._active, null);
Assert.equal(Services.ppmm.sharedData.get("FormAutofill:enabled"), undefined);
Assert.equal(Services.ppmm.initialProcessData.autofillEnabled, undefined);
await formAutofillParent.init();
// init shouldn't call updateStatus since that requires storage which will
// lead to startup time regressions.
Assert.equal(formAutofillParent._updateStatus.called, false);
Assert.equal(Services.ppmm.sharedData.get("FormAutofill:enabled"), undefined);
Assert.equal(Services.ppmm.initialProcessData.autofillEnabled, undefined);
// Initialize profile storage
await formAutofillParent.formAutofillStorage.initialize();
// Upon first initializing profile storage, status should be computed.
Assert.equal(formAutofillParent._updateStatus.called, true);
Assert.equal(Services.ppmm.sharedData.get("FormAutofill:enabled"), false);
Assert.equal(Services.ppmm.initialProcessData.autofillEnabled, false);
formAutofillParent._uninit();
});

Просмотреть файл

@ -491,12 +491,12 @@ const TESTCASES = [
},
];
add_task(async function handle_invalid_form() {
add_task(async function handle_earlyformsubmit_event() {
info("Starting testcase: Test an invalid form element");
let fakeForm = MOCK_DOC.createElement("form");
sinon.spy(FormAutofillContent, "_onFormSubmit");
FormAutofillContent.formSubmitted(fakeForm, null);
Assert.equal(FormAutofillContent.notify(fakeForm), true);
Assert.equal(FormAutofillContent._onFormSubmit.called, false);
FormAutofillContent._onFormSubmit.restore();
});
@ -525,14 +525,14 @@ add_task(async function autofill_disabled() {
// are disabled.
Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
FormAutofillContent.formSubmitted(form, null);
FormAutofillContent.notify(form);
Assert.equal(FormAutofillContent._onFormSubmit.called, false);
FormAutofillContent._onFormSubmit.reset();
// "_onFormSubmit" should be called as usual.
Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled");
Services.prefs.clearUserPref("extensions.formautofill.creditCards.enabled");
FormAutofillContent.formSubmitted(form, null);
FormAutofillContent.notify(form);
Assert.equal(FormAutofillContent._onFormSubmit.called, true);
Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []);
Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []);
@ -540,7 +540,7 @@ add_task(async function autofill_disabled() {
// "address" should be empty if "addresses" pref is disabled.
Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
FormAutofillContent.formSubmitted(form, null);
FormAutofillContent.notify(form);
Assert.equal(FormAutofillContent._onFormSubmit.called, true);
Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []);
Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []);
@ -549,7 +549,7 @@ add_task(async function autofill_disabled() {
// "creditCard" should be empty if "creditCards" pref is disabled.
Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
FormAutofillContent.formSubmitted(form, null);
FormAutofillContent.notify(form);
Assert.deepEqual(FormAutofillContent._onFormSubmit.called, true);
Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []);
Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []);
@ -582,7 +582,7 @@ TESTCASES.forEach(testcase => {
let element = MOCK_DOC.getElementById(TARGET_ELEMENT_ID);
FormAutofillContent.identifyAutofillFields(element);
FormAutofillContent.formSubmitted(form, null);
FormAutofillContent.notify(form);
Assert.equal(FormAutofillContent._onFormSubmit.called,
testcase.expectedResult.formSubmission,

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Test for keeping the valid fields information in sharedData.
* Test for keeping the valid fields information in initialProcessData.
*/
"use strict";
@ -54,7 +54,7 @@ add_task(async function test_profileSavedFieldNames_update() {
// The set is empty if there's no profile in the store.
formAutofillParent._updateSavedFieldNames();
Assert.equal(Services.ppmm.sharedData.get("FormAutofill:savedFieldNames").size, 0);
Assert.equal(Services.ppmm.initialProcessData.autofillSavedFieldNames.size, 0);
// 2 profiles with 4 valid fields.
formAutofillParent.formAutofillStorage.addresses._data = [{
@ -81,7 +81,7 @@ add_task(async function test_profileSavedFieldNames_update() {
formAutofillParent._updateSavedFieldNames();
let autofillSavedFieldNames = Services.ppmm.sharedData.get("FormAutofill:savedFieldNames");
let autofillSavedFieldNames = Services.ppmm.initialProcessData.autofillSavedFieldNames;
Assert.equal(autofillSavedFieldNames.size, 4);
Assert.equal(autofillSavedFieldNames.has("organization"), true);
Assert.equal(autofillSavedFieldNames.has("street-address"), true);

Просмотреть файл

@ -92,6 +92,9 @@ static const nsAttrValue::EnumTable kFormAutocompleteTable[] = {
static const nsAttrValue::EnumTable* kFormDefaultAutocomplete =
&kFormAutocompleteTable[0];
bool HTMLFormElement::gFirstFormSubmitted = false;
bool HTMLFormElement::gPasswordManagerInitialized = false;
HTMLFormElement::HTMLFormElement(
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
: nsGenericHTMLElement(std::move(aNodeInfo)),
@ -850,6 +853,14 @@ nsresult HTMLFormElement::DoSecureToInsecureSubmitCheck(nsIURI* aActionURL,
nsresult HTMLFormElement::NotifySubmitObservers(nsIURI* aActionURL,
bool* aCancelSubmit,
bool aEarlyNotify) {
// If this is the first form, bring alive the first form submit
// category observers
if (!gFirstFormSubmitted) {
gFirstFormSubmitted = true;
NS_CreateServicesFromCategory(NS_FIRST_FORMSUBMIT_CATEGORY, nullptr,
NS_FIRST_FORMSUBMIT_CATEGORY);
}
if (!aEarlyNotify) {
nsresult rv = DoSecureToInsecureSubmitCheck(aActionURL, aCancelSubmit);
if (NS_FAILED(rv)) {
@ -860,16 +871,44 @@ nsresult HTMLFormElement::NotifySubmitObservers(nsIURI* aActionURL,
}
}
bool defaultAction = true;
nsresult rv = nsContentUtils::DispatchEventOnlyToChrome(
OwnerDoc(), static_cast<nsINode*>(this),
aEarlyNotify ? NS_LITERAL_STRING("DOMFormBeforeSubmit")
: NS_LITERAL_STRING("DOMFormSubmit"),
CanBubble::eYes, Cancelable::eYes, &defaultAction);
*aCancelSubmit = !defaultAction;
if (*aCancelSubmit) {
return NS_OK;
// Notify observers that the form is being submitted.
nsCOMPtr<nsIObserverService> service =
mozilla::services::GetObserverService();
if (!service) return NS_ERROR_FAILURE;
nsCOMPtr<nsISimpleEnumerator> theEnum;
nsresult rv = service->EnumerateObservers(
aEarlyNotify ? NS_EARLYFORMSUBMIT_SUBJECT : NS_FORMSUBMIT_SUBJECT,
getter_AddRefs(theEnum));
NS_ENSURE_SUCCESS(rv, rv);
if (theEnum) {
nsCOMPtr<nsISupports> inst;
*aCancelSubmit = false;
// XXXbz what do the submit observers actually want? The window
// of the document this is shown in? Or something else?
// sXBL/XBL2 issue
nsCOMPtr<nsPIDOMWindowOuter> window = OwnerDoc()->GetWindow();
bool loop = true;
while (NS_SUCCEEDED(theEnum->HasMoreElements(&loop)) && loop) {
theEnum->GetNext(getter_AddRefs(inst));
nsCOMPtr<nsIFormSubmitObserver> formSubmitObserver(
do_QueryInterface(inst));
if (formSubmitObserver) {
rv = formSubmitObserver->Notify(
this, window ? window->GetCurrentInnerWindow() : nullptr,
aActionURL, aCancelSubmit);
NS_ENSURE_SUCCESS(rv, rv);
}
if (*aCancelSubmit) {
return NS_OK;
}
}
}
return rv;
}
@ -1085,8 +1124,16 @@ nsresult HTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild,
int32_t type = aChild->ControlType();
// If it is a password control, inform the password manager.
//
// If it is a password control, and the password manager has not yet been
// initialized, initialize the password manager
//
if (type == NS_FORM_INPUT_PASSWORD) {
if (!gPasswordManagerInitialized) {
gPasswordManagerInitialized = true;
NS_CreateServicesFromCategory(NS_PASSWORDMANAGER_CATEGORY, nullptr,
NS_PASSWORDMANAGER_CATEGORY);
}
PostPasswordEvent();
}

Просмотреть файл

@ -602,6 +602,12 @@ class HTMLFormElement final : public nsGenericHTMLElement,
*/
bool mEverTriedInvalidSubmit;
protected:
/** Detection of first form to notify observers */
static bool gFirstFormSubmitted;
/** Detection of first password input to initialize the password manager */
static bool gPasswordManagerInitialized;
private:
~HTMLFormElement();
};

Просмотреть файл

@ -16,10 +16,16 @@ webidl Element;
[scriptable, uuid(867cb7e7-835d-408b-9788-d2834d284e03)]
interface nsIFormSubmitObserver: nsISupports
{
void notify(in HTMLFormElement formNode, in mozIDOMWindow window, in nsIURI actionURL, out boolean cancelSubmit);
void notifyInvalidSubmit(in HTMLFormElement formNode,
in Array<Element> invalidElements);
};
%{C++
#define NS_FORMSUBMIT_SUBJECT "formsubmit"
#define NS_EARLYFORMSUBMIT_SUBJECT "earlyformsubmit"
#define NS_FIRST_FORMSUBMIT_CATEGORY "firstformsubmit"
#define NS_PASSWORDMANAGER_CATEGORY "passwordmanager"
#define NS_INVALIDFORMSUBMIT_SUBJECT "invalidformsubmit"
%}

Просмотреть файл

@ -226,12 +226,6 @@ BrowserCLH.prototype = {
// NOTE: Much of this logic is duplicated in browser/base/content/content.js
// for desktop.
aWindow.addEventListener("DOMFormBeforeSubmit", event => {
if (shouldIgnoreLoginManagerEvent(event)) {
return;
}
this.LoginManagerContent.onDOMFormBeforeSubmit(event);
});
aWindow.addEventListener("DOMFormHasPassword", event => {
if (shouldIgnoreLoginManagerEvent(event)) {
return;

Просмотреть файл

@ -51,9 +51,27 @@ var gLastRightClickTimeStamp = Number.NEGATIVE_INFINITY;
var observer = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
Ci.nsIFormSubmitObserver,
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference]),
// nsIFormSubmitObserver
notify(formElement, aWindow, actionURI) {
log("observer notified for form submission.");
// We're invoked before the content's |onsubmit| handlers, so we
// can grab form data before it might be modified (see bug 257781).
try {
let formLike = LoginFormFactory.createFromForm(formElement);
LoginManagerContent._onFormSubmit(formLike);
} catch (e) {
log("Caught error in onFormSubmit(", e.lineNumber, "):", e.message);
Cu.reportError(e);
}
return true; // Always return true, or form submit will be canceled.
},
// nsIWebProgressListener
onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
@ -127,6 +145,7 @@ var observer = {
},
};
Services.obs.addObserver(observer, "earlyformsubmit");
// This object maps to the "child" process (even in the single-process case).
var LoginManagerContent = {
@ -341,18 +360,6 @@ var LoginManagerContent = {
}
},
onDOMFormBeforeSubmit(event) {
if (!event.isTrusted) {
return;
}
// We're invoked before the content's |submit| event handlers, so we
// can grab form data before it might be modified (see bug 257781).
log("notified before form submission");
let formLike = LoginFormFactory.createFromForm(event.target);
LoginManagerContent._onFormSubmit(formLike);
},
onDOMFormHasPassword(event) {
if (!event.isTrusted) {
return;
@ -896,7 +903,7 @@ var LoginManagerContent = {
if (ChromeUtils.getClassName(formRoot) === "HTMLFormElement") {
// For now only perform capture upon navigation for FormLike's without
// a <form> to avoid capture from both a DOMFormBeforeSubmit event and
// a <form> to avoid capture from both an earlyformsubmit and
// navigation for the same "form".
log("Ignoring navigation for the form root to avoid multiple prompts " +
"since it was for a real <form>");

Просмотреть файл

@ -47,7 +47,8 @@ FormHistoryStartup.prototype = {
Services.obs.addObserver(this, "idle-daily", true);
Services.obs.addObserver(this, "formhistory-expire-now", true);
Services.mm.addMessageListener("FormHistory:FormSubmitEntries", this);
Services.ppmm.loadProcessScript("chrome://satchel/content/formSubmitListener.js", true);
Services.ppmm.addMessageListener("FormHistory:FormSubmitEntries", this);
// For each of these messages, we could receive them from content,
// or we might receive them from the ppmm if the searchbar is

Просмотреть файл

@ -1,152 +0,0 @@
/* 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";
var EXPORTED_SYMBOLS = ["FormSubmitChild"];
const {ActorChild} = ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "CreditCard",
"resource://gre/modules/CreditCard.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
class FormSubmitChild extends ActorChild {
constructor(dispatcher) {
super(dispatcher);
this.QueryInterface = ChromeUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference,
]);
Services.prefs.addObserver("browser.formfill.", this);
this.updatePrefs();
}
cleanup() {
super.cleanup();
Services.prefs.removeObserver("browser.formfill.", this);
}
updatePrefs() {
this.debug = Services.prefs.getBoolPref("browser.formfill.debug");
this.enabled = Services.prefs.getBoolPref("browser.formfill.enable");
}
log(message) {
if (!this.debug) {
return;
}
dump("satchelFormListener: " + message + "\n");
Services.console.logStringMessage("satchelFormListener: " + message);
}
/* ---- nsIObserver interface ---- */
observe(subject, topic, data) {
if (topic == "nsPref:changed") {
this.updatePrefs();
} else {
this.log("Oops! Unexpected notification: " + topic);
}
}
handleEvent(event) {
switch (event.type) {
case "DOMFormBeforeSubmit": {
this.onDOMFormBeforeSubmit(event);
break;
}
default: {
throw new Error("Unexpected event");
}
}
}
onDOMFormBeforeSubmit(event) {
let form = event.target;
if (!this.enabled || PrivateBrowsingUtils.isContentWindowPrivate(form.ownerGlobal)) {
return;
}
this.log("Form submit observer notified.");
if (form.hasAttribute("autocomplete") &&
form.getAttribute("autocomplete").toLowerCase() == "off") {
return;
}
let entries = [];
for (let input of form.elements) {
if (ChromeUtils.getClassName(input) !== "HTMLInputElement") {
continue;
}
// Only use inputs that hold text values (not including type="password")
if (!input.mozIsTextField(true)) {
continue;
}
// Don't save fields that were previously type=password such as on sites
// that allow the user to toggle password visibility.
if (input.hasBeenTypePassword) {
continue;
}
// Bug 394612: If Login Manager marked this input, don't save it.
// The login manager will deal with remembering it.
// Don't save values when @autocomplete is "off" or has a sensitive field name.
let autocompleteInfo = input.getAutocompleteInfo();
if (autocompleteInfo && !autocompleteInfo.canAutomaticallyPersist) {
continue;
}
let value = input.value.trim();
// Don't save empty or unchanged values.
if (!value || value == input.defaultValue.trim()) {
continue;
}
// Don't save credit card numbers.
if (CreditCard.isValidNumber(value)) {
this.log("skipping saving a credit card number");
continue;
}
let name = input.name || input.id;
if (!name) {
continue;
}
if (name == "searchbar-history") {
this.log('addEntry for input name "' + name + '" is denied');
continue;
}
// Limit stored data to 200 characters.
if (name.length > 200 || value.length > 200) {
this.log("skipping input that has a name/value too large");
continue;
}
// Limit number of fields stored per form.
if (entries.length >= 100) {
this.log("not saving any more entries for this form.");
break;
}
entries.push({ name, value });
}
if (entries.length) {
this.log("sending entries to parent process for form " + form.id);
this.sendAsyncMessage("FormHistory:FormSubmitEntries", entries);
}
}
}

Просмотреть файл

@ -0,0 +1,150 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-env mozilla/frame-script */
ChromeUtils.defineModuleGetter(this, "CreditCard",
"resource://gre/modules/CreditCard.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
(function() {
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
let satchelFormListener = {
QueryInterface: ChromeUtils.generateQI([
Ci.nsIFormSubmitObserver,
Ci.nsIObserver,
Ci.nsISupportsWeakReference,
]),
debug: true,
enabled: true,
init() {
Services.obs.addObserver(this, "earlyformsubmit");
Services.obs.addObserver(this, "xpcom-shutdown");
Services.prefs.addObserver("browser.formfill.", this);
this.updatePrefs();
},
updatePrefs() {
this.debug = Services.prefs.getBoolPref("browser.formfill.debug");
this.enabled = Services.prefs.getBoolPref("browser.formfill.enable");
},
log(message) {
if (!this.debug) {
return;
}
dump("satchelFormListener: " + message + "\n");
Services.console.logStringMessage("satchelFormListener: " + message);
},
/* ---- nsIObserver interface ---- */
observe(subject, topic, data) {
if (topic == "nsPref:changed") {
this.updatePrefs();
} else if (topic == "xpcom-shutdown") {
Services.obs.removeObserver(this, "earlyformsubmit");
Services.obs.removeObserver(this, "xpcom-shutdown");
Services.prefs.removeObserver("browser.formfill.", this);
} else {
this.log("Oops! Unexpected notification: " + topic);
}
},
/* ---- nsIFormSubmitObserver interfaces ---- */
notify(form, domWin, actionURI, cancelSubmit) {
try {
if (!this.enabled || PrivateBrowsingUtils.isContentWindowPrivate(domWin)) {
return;
}
this.log("Form submit observer notified.");
if (form.hasAttribute("autocomplete") &&
form.getAttribute("autocomplete").toLowerCase() == "off") {
return;
}
let entries = [];
for (let i = 0; i < form.elements.length; i++) {
let input = form.elements[i];
if (ChromeUtils.getClassName(input) !== "HTMLInputElement") {
continue;
}
// Only use inputs that hold text values (not including type="password")
if (!input.mozIsTextField(true)) {
continue;
}
// Don't save fields that were previously type=password such as on sites
// that allow the user to toggle password visibility.
if (input.hasBeenTypePassword) {
continue;
}
// Bug 394612: If Login Manager marked this input, don't save it.
// The login manager will deal with remembering it.
// Don't save values when @autocomplete is "off" or has a sensitive field name.
let autocompleteInfo = input.getAutocompleteInfo();
if (autocompleteInfo && !autocompleteInfo.canAutomaticallyPersist) {
continue;
}
let value = input.value.trim();
// Don't save empty or unchanged values.
if (!value || value == input.defaultValue.trim()) {
continue;
}
// Don't save credit card numbers.
if (CreditCard.isValidNumber(value)) {
this.log("skipping saving a credit card number");
continue;
}
let name = input.name || input.id;
if (!name) {
continue;
}
if (name == "searchbar-history") {
this.log('addEntry for input name "' + name + '" is denied');
continue;
}
// Limit stored data to 200 characters.
if (name.length > 200 || value.length > 200) {
this.log("skipping input that has a name/value too large");
continue;
}
// Limit number of fields stored per form.
if (entries.length >= 100) {
this.log("not saving any more entries for this form.");
break;
}
entries.push({ name, value });
}
if (entries.length) {
this.log("sending entries to parent process for form " + form.id);
sendAsyncMessage("FormHistory:FormSubmitEntries", entries);
}
} catch (e) {
this.log("notify failed: " + e);
}
},
};
satchelFormListener.init();
})();

Просмотреть файл

@ -0,0 +1,7 @@
# 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/.
toolkit.jar:
% content satchel %content/satchel/
content/satchel/formSubmitListener.js

Просмотреть файл

@ -40,8 +40,6 @@ XPCOM_MANIFESTS += [
'components.conf',
]
FINAL_TARGET_FILES.actors += [
'FormSubmitChild.jsm',
]
FINAL_LIBRARY = 'xul'
JAR_MANIFESTS += ['jar.mn']

Просмотреть файл

@ -31,12 +31,10 @@ function runTests() {
var target = document.getElementById("input");
// Register a word to the form history.
let chromeScript = SpecialPowers.loadChromeScript(function addEntry() {
let {FormHistory} = ChromeUtils.import("resource://gre/modules/FormHistory.jsm");
FormHistory.update({ op: "add", fieldname: "test", value: "Mozilla" });
});
chromeScript.destroy();
target.focus();
target.value = "Mozilla";
synthesizeKey("KEY_Enter");
target.value = "";
new nsDoTestsForAutoCompleteWithComposition(
"Testing on HTML input (asynchronously search)",

Просмотреть файл

@ -23,13 +23,9 @@ SimpleTest.waitForExplicitFinish();
async function registerWord(aTarget, aAutoCompleteController) {
// Register a word to the form history.
let chromeScript = SpecialPowers.loadChromeScript(function addEntry() {
let {FormHistory} = ChromeUtils.import("resource://gre/modules/FormHistory.jsm");
FormHistory.update({ op: "add", fieldname: "test", value: "Mozilla" });
});
aTarget.focus();
aTarget.value = "Mozilla";
synthesizeKey("KEY_Enter");
await waitForCondition(() => {
if (aAutoCompleteController.searchStatus == aAutoCompleteController.STATUS_NONE ||
aAutoCompleteController.searchStatus == aAutoCompleteController.STATUS_COMPLETE_NO_MATCH) {
@ -37,7 +33,6 @@ async function registerWord(aTarget, aAutoCompleteController) {
}
return aAutoCompleteController.matchCount > 0;
});
chromeScript.destroy();
aTarget.value = "";
synthesizeKey("KEY_Escape");
}

Просмотреть файл

@ -186,16 +186,6 @@ let ACTORS = {
},
},
FormSubmit: {
child: {
module: "resource://gre/actors/FormSubmitChild.jsm",
allFrames: true,
events: {
"DOMFormBeforeSubmit": {},
},
},
},
KeyPressEventModelChecker: {
child: {
module: "resource://gre/actors/KeyPressEventModelCheckerChild.jsm",

Просмотреть файл

@ -82,18 +82,25 @@ var CreatedNavigationTargetListener = {
};
var FormSubmitListener = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
Ci.nsIFormSubmitObserver,
Ci.nsISupportsWeakReference]),
init() {
this.formSubmitWindows = new WeakSet();
addEventListener("DOMFormBeforeSubmit", this);
Services.obs.addObserver(FormSubmitListener, "earlyformsubmit");
},
uninit() {
removeEventListener("DOMFormBeforeSubmit", this);
Services.obs.removeObserver(FormSubmitListener, "earlyformsubmit");
this.formSubmitWindows = new WeakSet();
},
handleEvent({target: form}) {
this.formSubmitWindows.add(form.ownerGlobal);
notify: function(form, window, actionURI) {
try {
this.formSubmitWindows.add(window);
} catch (e) {
Cu.reportError("Error in FormSubmitListener.notify");
}
},
hasAndForget: function(window) {