mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-11 17:57:32 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
272
toolkit/components/formautofill/FormAutofillContentService.js
Normal file
272
toolkit/components/formautofill/FormAutofillContentService.js
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
/* 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/. */
|
||||
|
||||
/*
|
||||
* Implements a service used by DOM content to request Form Autofill, in
|
||||
* particular when the requestAutocomplete method of Form objects is invoked.
|
||||
*
|
||||
* See the nsIFormAutofillContentService documentation for details.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofill",
|
||||
"resource://gre/modules/FormAutofill.jsm");
|
||||
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;
|
||||
|
||||
this.fieldDetails = [];
|
||||
}
|
||||
|
||||
FormHandler.prototype = {
|
||||
/**
|
||||
* DOM Form element to which this object is attached.
|
||||
*/
|
||||
form: null,
|
||||
|
||||
/**
|
||||
* nsIDOMWindow to which this object is attached.
|
||||
*/
|
||||
window: null,
|
||||
|
||||
/**
|
||||
* Array of collected data about relevant form fields. Each item is an object
|
||||
* storing the identifying details of the field and a reference to the
|
||||
* originally associated element from the form.
|
||||
*
|
||||
* The "section", "addressType", "contactType", and "fieldName" values are
|
||||
* used to identify the exact field when the serializable data is received
|
||||
* from the requestAutocomplete user interface. There cannot be multiple
|
||||
* fields which have the same exact combination of these values.
|
||||
*
|
||||
* A direct reference to the associated element cannot be sent to the user
|
||||
* interface because processing may be done in the parent process.
|
||||
*/
|
||||
fieldDetails: 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 {
|
||||
reason = yield this.promiseRequestAutocomplete();
|
||||
} 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);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Handles requestAutocomplete and returns the outcome when finished.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The "reason" value indicating the outcome of the
|
||||
* requestAutocomplete operation, including "success" if the
|
||||
* operation completed successfully.
|
||||
*/
|
||||
promiseRequestAutocomplete: Task.async(function* () {
|
||||
let data = this.collectFormFields();
|
||||
if (!data) {
|
||||
return "disabled";
|
||||
}
|
||||
|
||||
// Access the frame message manager of the window starting the request.
|
||||
let rootDocShell = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.sameTypeRootTreeItem
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
let frameMM = rootDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
|
||||
// We need to set up a temporary message listener for our result before we
|
||||
// send the request to the parent process. At present, there is no check
|
||||
// for reentrancy (bug 1020459), thus it is possible that we'll receive a
|
||||
// message for a different request, but this will not be normally allowed.
|
||||
let promiseRequestAutocompleteResult = new Promise((resolve, reject) => {
|
||||
frameMM.addMessageListener("FormAutofill:RequestAutocompleteResult",
|
||||
function onResult(aMessage) {
|
||||
frameMM.removeMessageListener(
|
||||
"FormAutofill:RequestAutocompleteResult", onResult);
|
||||
// Exceptions in the parent process are serialized and propagated in
|
||||
// the response message that we received.
|
||||
if ("exception" in aMessage.data) {
|
||||
reject(aMessage.data.exception);
|
||||
} else {
|
||||
resolve(aMessage.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Send the message to the parent process, and wait for the result. This
|
||||
// will throw an exception if one occurred in the parent process.
|
||||
frameMM.sendAsyncMessage("FormAutofill:RequestAutocomplete", data);
|
||||
let result = yield promiseRequestAutocompleteResult;
|
||||
if (result.canceled) {
|
||||
return "cancel";
|
||||
}
|
||||
|
||||
this.autofillFormFields(result);
|
||||
|
||||
return "success";
|
||||
}),
|
||||
|
||||
/**
|
||||
* Returns information from the form about fields that can be autofilled, and
|
||||
* populates the fieldDetails array on this object accordingly.
|
||||
*
|
||||
* @returns Serializable data structure that can be sent to the user
|
||||
* interface, or null if the operation failed because the constraints
|
||||
* on the allowed fields were not honored.
|
||||
*/
|
||||
collectFormFields: 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;
|
||||
}
|
||||
|
||||
// Store the association between the field metadata and the element.
|
||||
if (this.fieldDetails.some(f => f.section == info.section &&
|
||||
f.addressType == info.addressType &&
|
||||
f.contactType == info.contactType &&
|
||||
f.fieldName == info.fieldName)) {
|
||||
// A field with the same identifier already exists.
|
||||
return null;
|
||||
}
|
||||
this.fieldDetails.push({
|
||||
section: info.section,
|
||||
addressType: info.addressType,
|
||||
contactType: info.contactType,
|
||||
fieldName: info.fieldName,
|
||||
element: element,
|
||||
});
|
||||
|
||||
// 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;
|
||||
},
|
||||
|
||||
/**
|
||||
* Processes form fields that can be autofilled, and populates them with the
|
||||
* data provided by RequestAutocompleteUI.
|
||||
*
|
||||
* @param aAutofillResult
|
||||
* Data returned by the user interface.
|
||||
* {
|
||||
* fields: [
|
||||
* section: Value originally provided to the user interface.
|
||||
* addressType: Value originally provided to the user interface.
|
||||
* contactType: Value originally provided to the user interface.
|
||||
* fieldName: Value originally provided to the user interface.
|
||||
* value: String with which the field should be updated.
|
||||
* ],
|
||||
* }
|
||||
*/
|
||||
autofillFormFields: function (aAutofillResult) {
|
||||
for (let field of aAutofillResult.fields) {
|
||||
// Get the field details, if it was processed by the user interface.
|
||||
let fieldDetail = this.fieldDetails
|
||||
.find(f => f.section == field.section &&
|
||||
f.addressType == field.addressType &&
|
||||
f.contactType == field.contactType &&
|
||||
f.fieldName == field.fieldName);
|
||||
if (!fieldDetail) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fieldDetail.element.value = field.value;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
}
|
||||
|
||||
FormAutofillContentService.prototype = {
|
||||
classID: Components.ID("{ed9c2c3c-3f86-4ae5-8e31-10f71b0f19e6}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormAutofillContentService]),
|
||||
|
||||
// nsIFormAutofillContentService
|
||||
requestAutocomplete: function (aForm, aWindow) {
|
||||
new FormHandler(aForm, aWindow).handleRequestAutocomplete()
|
||||
.catch(Cu.reportError);
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FormAutofillContentService]);
|
||||
Loading…
Add table
Add a link
Reference in a new issue