Bug 1020602 - Send serialized elements to requestAutocomplete UI. r=MattN

This commit is contained in:
Brian Nicholson
2014-06-25 13:53:00 +01:00
parent 3c13f3ea06
commit 1f2f9c90d0
3 changed files with 139 additions and 32 deletions

View File

@@ -21,6 +21,124 @@ XPCOMUtils.defineLazyModuleGetter(this, "FormAutofill",
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
/**
* Handles requestAutocomplete for a DOM Form element.
*/
function FormHandler(aForm, aWindow) {
this.form = aForm;
this.window = aWindow;
}
FormHandler.prototype = {
/**
* DOM Form element to which this object is attached.
*/
form: null,
/**
* nsIDOMWindow to which this object is attached.
*/
window: null,
/**
* Handles requestAutocomplete and generates the DOM events when finished.
*/
handleRequestAutocomplete: Task.async(function* () {
// Start processing the request asynchronously. At the end, the "reason"
// variable will contain the outcome of the operation, where an empty
// string indicates that an unexpected exception occurred.
let reason = "";
try {
let data = this.collectFormElements();
let ui = yield FormAutofill.integration.createRequestAutocompleteUI(data);
let result = yield ui.show();
// At present, we only have cancellation and success cases, since we
// don't do any validation or precondition check.
reason = result.canceled ? "cancel" : "success";
} catch (ex) {
Cu.reportError(ex);
}
// The type of event depends on whether this is a success condition.
let event = (reason == "success")
? new this.window.Event("autocomplete", { bubbles: true })
: new this.window.AutocompleteErrorEvent("autocompleteerror",
{ bubbles: true,
reason: reason });
yield this.waitForTick();
this.form.dispatchEvent(event);
}),
/**
* Collects information from the form about fields that can be autofilled, and
* returns an object that can be used to build RequestAutocompleteUI.
*/
collectFormElements: function () {
let autofillData = {
sections: [],
};
for (let element of this.form.elements) {
// Query the interface and exclude elements that cannot be autocompleted.
if (!(element instanceof Ci.nsIDOMHTMLInputElement)) {
continue;
}
// Exclude elements to which no autocomplete field has been assigned.
let info = element.getAutocompleteInfo();
if (!info.fieldName || ["on", "off"].indexOf(info.fieldName) != -1) {
continue;
}
// The first level is the custom section.
let section = autofillData.sections
.find(s => s.name == info.section);
if (!section) {
section = {
name: info.section,
addressSections: [],
};
autofillData.sections.push(section);
}
// The second level is the address section.
let addressSection = section.addressSections
.find(s => s.addressType == info.addressType);
if (!addressSection) {
addressSection = {
addressType: info.addressType,
fields: [],
};
section.addressSections.push(addressSection);
}
// The third level contains all the fields within the section.
let field = {
fieldName: info.fieldName,
contactType: info.contactType,
};
addressSection.fields.push(field);
}
return autofillData;
},
/**
* Waits for one tick of the event loop before resolving the returned promise.
*/
waitForTick: function () {
return new Promise(function (resolve) {
Services.tm.currentThread.dispatch(resolve, Ci.nsIThread.DISPATCH_NORMAL);
});
},
};
/**
* Implements a service used by DOM content to request Form Autofill, in
* particular when the requestAutocomplete method of Form objects is invoked.
*/
function FormAutofillContentService() {
}
@@ -30,33 +148,8 @@ FormAutofillContentService.prototype = {
// nsIFormAutofillContentService
requestAutocomplete: function (aForm, aWindow) {
Task.spawn(function* () {
// Start processing the request asynchronously. At the end, the "reason"
// variable will contain the outcome of the operation, where an empty
// string indicates that an unexpected exception occurred.
let reason = "";
try {
let ui = yield FormAutofill.integration.createRequestAutocompleteUI({});
let result = yield ui.show();
// At present, we only have cancellation and success cases, since we
// don't do any validation or precondition check.
reason = result.canceled ? "cancel" : "success";
} catch (ex) {
Cu.reportError(ex);
}
// The type of event depends on whether this is a success condition.
let event = (reason == "success")
? new aWindow.Event("autocomplete", { bubbles: true })
: new aWindow.AutocompleteErrorEvent("autocompleteerror",
{ bubbles: true,
reason: reason });
// Ensure the event is always dispatched on the next tick.
Services.tm.currentThread.dispatch(() => aForm.dispatchEvent(event),
Ci.nsIThread.DISPATCH_NORMAL);
}.bind(this)).catch(Cu.reportError);
new FormHandler(aForm, aWindow).handleRequestAutocomplete()
.catch(Cu.reportError);
},
};

View File

@@ -37,14 +37,26 @@ this.FormAutofillIntegration = {
/**
* Creates a new RequestAutocompleteUI object.
*
* @param aProperties
* Provides the initial properties for the newly created object.
* @param aAutofillData
* Provides the initial data required to display the user interface.
* {
* sections: [{
* name: User-specified section name, or empty string.
* addressSections: [{
* addressType: "shipping", "billing", or empty string.
* fields: [{
* fieldName: Type of information requested, like "email".
* contactType: For example "work", "home", or empty string.
* }],
* }],
* }],
* }
*
* @return {Promise}
* @resolves The newly created RequestAutocompleteUI object.
* @rejects JavaScript exception.
*/
createRequestAutocompleteUI: Task.async(function* (aProperties) {
return new RequestAutocompleteUI(aProperties);
createRequestAutocompleteUI: Task.async(function* (aAutofillData) {
return new RequestAutocompleteUI(aAutofillData);
}),
};

View File

@@ -25,7 +25,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
/**
* Handles the requestAutocomplete user interface.
*/
this.RequestAutocompleteUI = function (aProperties) {
this.RequestAutocompleteUI = function (aAutofillData) {
Services.console.logStringMessage("rAc UI request: " +
JSON.stringify(aAutofillData));
}
this.RequestAutocompleteUI.prototype = {