зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1019483 - (Part 1) Create interface to manage autofill profiles. r=MattN
MozReview-Commit-ID: KrGSPz7B108 --HG-- extra : rebase_source : 29d93d82b5240420121024ec9fb7afea69d5600b
This commit is contained in:
Родитель
f55fc8d7c5
Коммит
cf289cb4ce
|
@ -132,8 +132,6 @@ var whitelist = new Set([
|
|||
platforms: ["linux", "win"]},
|
||||
// Bug 1320058
|
||||
{file: "chrome://browser/skin/preferences/saveFile.png", platforms: ["win"]},
|
||||
// Bug 1348369
|
||||
{file: "chrome://formautofill/content/editProfile.xhtml"},
|
||||
// Bug 1316187
|
||||
{file: "chrome://global/content/customizeToolbar.xul"},
|
||||
// Bug 1343837
|
||||
|
|
|
@ -76,7 +76,9 @@ FormAutofillParent.prototype = {
|
|||
this._profileStore.initialize();
|
||||
|
||||
Services.obs.addObserver(this, "advanced-pane-loaded", false);
|
||||
Services.ppmm.addMessageListener("FormAutofill:GetProfiles", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:SaveProfile", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:RemoveProfiles", this);
|
||||
|
||||
// Observing the pref and storage changes
|
||||
Services.prefs.addObserver(ENABLED_PREF, this, false);
|
||||
|
@ -131,17 +133,10 @@ FormAutofillParent.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Add/remove message listener and broadcast the status to frames while the
|
||||
* form autofill status changed.
|
||||
* Broadcast the status to frames when the form autofill status changes.
|
||||
*/
|
||||
_onStatusChanged() {
|
||||
log.debug("_onStatusChanged: Status changed to", this._enabled);
|
||||
if (this._enabled) {
|
||||
Services.ppmm.addMessageListener("FormAutofill:GetProfiles", this);
|
||||
} else {
|
||||
Services.ppmm.removeMessageListener("FormAutofill:GetProfiles", this);
|
||||
}
|
||||
|
||||
Services.ppmm.broadcastAsyncMessage("FormAutofill:enabledStatus", this._enabled);
|
||||
// Sync process data autofillEnabled to make sure the value up to date
|
||||
// no matter when the new content process is initialized.
|
||||
|
@ -193,6 +188,10 @@ FormAutofillParent.prototype = {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "FormAutofill:RemoveProfiles": {
|
||||
data.guids.forEach(guid => this.getProfileStore().remove(guid));
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -220,6 +219,7 @@ FormAutofillParent.prototype = {
|
|||
|
||||
Services.ppmm.removeMessageListener("FormAutofill:GetProfiles", this);
|
||||
Services.ppmm.removeMessageListener("FormAutofill:SaveProfile", this);
|
||||
Services.ppmm.removeMessageListener("FormAutofill:RemoveProfiles", this);
|
||||
Services.obs.removeObserver(this, "advanced-pane-loaded");
|
||||
Services.prefs.removeObserver(ENABLED_PREF, this);
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@ this.EXPORTED_SYMBOLS = ["FormAutofillPreferences"];
|
|||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
const PREF_AUTOFILL_ENABLED = "browser.formautofill.enabled";
|
||||
const BUNDLE_URI = "chrome://formautofill/locale/formautofill.properties";
|
||||
const MANAGE_PROFILES_URL = "chrome://formautofill/content/manageProfiles.xhtml";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
@ -126,7 +127,7 @@ FormAutofillPreferences.prototype = {
|
|||
// Set preference directly instead of relying on <Preference>
|
||||
Services.prefs.setBoolPref(PREF_AUTOFILL_ENABLED, target.checked);
|
||||
} else if (target == this.refs.savedProfilesBtn) {
|
||||
// TODO: Open Saved Profiles dialog
|
||||
target.ownerGlobal.gSubDialog.open(MANAGE_PROFILES_URL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
body {
|
||||
font-size: 1rem;
|
||||
/* body needs padding until the edit profile dialog could be loaded as a
|
||||
stacked subdialog */
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
form,
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Profile Autofill - Edit Profile</title>
|
||||
<!-- common.css and dialog.css need to be included until this file can be
|
||||
- loaded as a stacked subdialog. -->
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css" />
|
||||
<link rel="stylesheet" href="chrome://browser/skin/preferences/in-content/dialog.css" />
|
||||
<link rel="stylesheet" href="chrome://formautofill/content/editProfile.css" />
|
||||
<script src="chrome://formautofill/content/editProfile.js"></script>
|
||||
</head>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/* 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/. */
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 3px 2em;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
fieldset > legend {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 0.4em 0.7em;
|
||||
font-size: 0.9em;
|
||||
color: #808080;
|
||||
background-color: var(--in-content-box-background-hover);
|
||||
border: 1px solid var(--in-content-box-border-color);
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
|
||||
option:nth-child(even) {
|
||||
background-color: -moz-oddtreerow;
|
||||
}
|
||||
|
||||
#profiles {
|
||||
font-size: 0.85em;
|
||||
width: 100%;
|
||||
height: 16.6em;
|
||||
border-top: none;
|
||||
border-radius: 0 0 2px 2px;
|
||||
}
|
||||
|
||||
#profiles > option {
|
||||
padding-inline-start: 0.7em;
|
||||
}
|
||||
|
||||
#controls-container {
|
||||
flex: 0 1 100%;
|
||||
justify-content: end;
|
||||
font-size: 0.9em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#remove {
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: auto;
|
||||
}
|
||||
|
||||
#edit {
|
||||
margin-inline-end: 0;
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
/* 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";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
const EDIT_PROFILE_URL = "chrome://formautofill/content/editProfile.xhtml";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
|
||||
|
||||
this.log = null;
|
||||
FormAutofillUtils.defineLazyLogGetter(this, "manageProfiles");
|
||||
|
||||
function ManageProfileDialog() {
|
||||
window.addEventListener("DOMContentLoaded", this, {once: true});
|
||||
}
|
||||
|
||||
ManageProfileDialog.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
|
||||
|
||||
_elements: {},
|
||||
|
||||
/**
|
||||
* Count the number of "formautofill-storage-changed" events epected to
|
||||
* receive to prevent repeatedly loading profiles.
|
||||
* @type {number}
|
||||
*/
|
||||
_pendingChangeCount: 0,
|
||||
|
||||
/**
|
||||
* Get the selected options on the profiles element.
|
||||
*
|
||||
* @returns {array<DOMElement>}
|
||||
*/
|
||||
get _selectedOptions() {
|
||||
return Array.from(this._elements.profiles.selectedOptions);
|
||||
},
|
||||
|
||||
init() {
|
||||
this._elements = {
|
||||
profiles: document.getElementById("profiles"),
|
||||
controlsContainer: document.getElementById("controls-container"),
|
||||
remove: document.getElementById("remove"),
|
||||
add: document.getElementById("add"),
|
||||
edit: document.getElementById("edit"),
|
||||
};
|
||||
this.attachEventListeners();
|
||||
},
|
||||
|
||||
uninit() {
|
||||
log.debug("uninit");
|
||||
this.detachEventListeners();
|
||||
this._elements = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load profiles and render them.
|
||||
*
|
||||
* @returns {promise}
|
||||
*/
|
||||
loadProfiles() {
|
||||
return this.getProfiles().then(profiles => {
|
||||
log.debug("profiles:", profiles);
|
||||
this.renderProfileElements(profiles);
|
||||
this.updateButtonsStates(this._selectedOptions.length);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get profiles from storage.
|
||||
*
|
||||
* @returns {promise}
|
||||
*/
|
||||
getProfiles() {
|
||||
return new Promise(resolve => {
|
||||
Services.cpmm.addMessageListener("FormAutofill:Profiles", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Profiles", getResult);
|
||||
resolve(result.data);
|
||||
});
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetProfiles", {});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the profiles onto the page while maintaining selected options if
|
||||
* they still exist.
|
||||
*
|
||||
* @param {array<object>} profiles
|
||||
*/
|
||||
renderProfileElements(profiles) {
|
||||
let selectedGuids = this._selectedOptions.map(option => option.value);
|
||||
this.clearProfileElements();
|
||||
for (let profile of profiles) {
|
||||
let option = new Option(this.getProfileLabel(profile),
|
||||
profile.guid,
|
||||
false,
|
||||
selectedGuids.includes(profile.guid));
|
||||
option.profile = profile;
|
||||
this._elements.profiles.appendChild(option);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all existing profile elements.
|
||||
*/
|
||||
clearProfileElements() {
|
||||
let parent = this._elements.profiles;
|
||||
while (parent.lastChild) {
|
||||
parent.removeChild(parent.lastChild);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove profiles by guids.
|
||||
* Keep track of the number of "formautofill-storage-changed" events to
|
||||
* ignore before loading profiles.
|
||||
*
|
||||
* @param {array<string>} guids
|
||||
*/
|
||||
removeProfiles(guids) {
|
||||
this._pendingChangeCount += guids.length - 1;
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:RemoveProfiles", {guids});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get profile display label. It should display up to two pieces of
|
||||
* information, separated by a comma.
|
||||
*
|
||||
* @param {object} profile
|
||||
* @returns {string}
|
||||
*/
|
||||
getProfileLabel(profile) {
|
||||
// TODO: Implement a smarter way for deciding what to display
|
||||
// as option text. Possibly improve the algorithm in
|
||||
// ProfileAutoCompleteResult.jsm and reuse it here.
|
||||
const fieldOrder = [
|
||||
"street-address", // Street address
|
||||
"address-level2", // City/Town
|
||||
"organization", // Company or organization name
|
||||
"address-level1", // Province/State (Standardized code if possible)
|
||||
"country", // Country
|
||||
"postal-code", // Postal code
|
||||
"tel", // Phone number
|
||||
"email", // Email address
|
||||
];
|
||||
|
||||
let parts = [];
|
||||
for (const fieldName of fieldOrder) {
|
||||
let string = profile[fieldName];
|
||||
if (string) {
|
||||
parts.push(string);
|
||||
}
|
||||
if (parts.length == 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parts.join(", ");
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the edit profile dialog to create/edit a profile.
|
||||
*
|
||||
* @param {object} profile [optional]
|
||||
*/
|
||||
openEditDialog(profile) {
|
||||
window.openDialog(EDIT_PROFILE_URL, null,
|
||||
"chrome,centerscreen,modal,width=600,height=370",
|
||||
profile);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable/disable the Edit and Remove buttons based on number of selected
|
||||
* options.
|
||||
*
|
||||
* @param {number} selectedCount
|
||||
*/
|
||||
updateButtonsStates(selectedCount) {
|
||||
log.debug("updateButtonsStates:", selectedCount);
|
||||
if (selectedCount == 0) {
|
||||
this._elements.edit.setAttribute("disabled", "disabled");
|
||||
this._elements.remove.setAttribute("disabled", "disabled");
|
||||
} else if (selectedCount == 1) {
|
||||
this._elements.edit.removeAttribute("disabled");
|
||||
this._elements.remove.removeAttribute("disabled");
|
||||
} else if (selectedCount > 1) {
|
||||
this._elements.edit.setAttribute("disabled", "disabled");
|
||||
this._elements.remove.removeAttribute("disabled");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle events
|
||||
*
|
||||
* @param {DOMEvent} event
|
||||
*/
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "DOMContentLoaded": {
|
||||
this.init();
|
||||
this.loadProfiles();
|
||||
break;
|
||||
}
|
||||
case "click": {
|
||||
this.handleClick(event);
|
||||
break;
|
||||
}
|
||||
case "change": {
|
||||
this.updateButtonsStates(this._selectedOptions.length);
|
||||
break;
|
||||
}
|
||||
case "unload": {
|
||||
this.uninit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle click events
|
||||
*
|
||||
* @param {DOMEvent} event
|
||||
*/
|
||||
handleClick(event) {
|
||||
if (event.target == this._elements.remove) {
|
||||
this.removeProfiles(this._selectedOptions.map(option => option.value));
|
||||
} else if (event.target == this._elements.add) {
|
||||
this.openEditDialog();
|
||||
} else if (event.target == this._elements.edit) {
|
||||
this.openEditDialog(this._selectedOptions[0].profile);
|
||||
}
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "formautofill-storage-changed": {
|
||||
if (this._pendingChangeCount) {
|
||||
this._pendingChangeCount -= 1;
|
||||
return;
|
||||
}
|
||||
this.loadProfiles();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach event listener
|
||||
*/
|
||||
attachEventListeners() {
|
||||
window.addEventListener("unload", this, {once: true});
|
||||
this._elements.profiles.addEventListener("change", this);
|
||||
this._elements.controlsContainer.addEventListener("click", this);
|
||||
Services.obs.addObserver(this, "formautofill-storage-changed", false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove event listener
|
||||
*/
|
||||
detachEventListeners() {
|
||||
this._elements.profiles.removeEventListener("change", this);
|
||||
this._elements.controlsContainer.removeEventListener("click", this);
|
||||
Services.obs.removeObserver(this, "formautofill-storage-changed");
|
||||
},
|
||||
};
|
||||
|
||||
new ManageProfileDialog();
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- 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/. -->
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Profile Autofill - Manage Profiles</title>
|
||||
<link rel="stylesheet" href="chrome://formautofill/content/manageProfiles.css" />
|
||||
<script src="chrome://formautofill/content/manageProfiles.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<fieldset>
|
||||
<legend>Profiles</legend>
|
||||
<select id="profiles" size="9" multiple="multiple"/>
|
||||
</fieldset>
|
||||
<div id="controls-container">
|
||||
<button id="remove" disabled="disabled">Remove</button>
|
||||
<button id="add">Add</button>
|
||||
<button id="edit" disabled="disabled">Edit</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -196,7 +196,7 @@ xul|menulist {
|
|||
}
|
||||
|
||||
html|button:enabled:hover,
|
||||
html|select:enabled:hover,
|
||||
html|select:not([size][multiple]):enabled:hover,
|
||||
xul|button:not([disabled="true"]):hover,
|
||||
xul|colorpicker[type="button"]:not([disabled="true"]):hover,
|
||||
xul|menulist:not([disabled="true"]):hover {
|
||||
|
@ -204,7 +204,7 @@ xul|menulist:not([disabled="true"]):hover {
|
|||
}
|
||||
|
||||
html|button:enabled:hover:active,
|
||||
html|select:enabled:hover:active,
|
||||
html|select:not([size][multiple]):enabled:hover:active,
|
||||
xul|button:not([disabled="true"]):hover:active,
|
||||
xul|colorpicker[type="button"]:not([disabled="true"]):hover:active,
|
||||
xul|menulist[open="true"]:not([disabled="true"]) {
|
||||
|
@ -711,6 +711,7 @@ xul|filefield + xul|button {
|
|||
|
||||
/* List boxes */
|
||||
|
||||
html|select[size][multiple],
|
||||
xul|richlistbox,
|
||||
xul|listbox {
|
||||
-moz-appearance: none;
|
||||
|
@ -720,6 +721,7 @@ xul|listbox {
|
|||
color: var(--in-content-text-color);
|
||||
}
|
||||
|
||||
html|select[size][multiple] > html|option,
|
||||
xul|treechildren::-moz-tree-row,
|
||||
xul|listbox xul|listitem {
|
||||
padding: 0.3em;
|
||||
|
@ -729,6 +731,7 @@ xul|listbox xul|listitem {
|
|||
background-image: none;
|
||||
}
|
||||
|
||||
html|select[size][multiple] > html|option:hover,
|
||||
xul|treechildren::-moz-tree-row(hover),
|
||||
xul|listbox xul|listitem:hover {
|
||||
background-color: var(--in-content-item-hover);
|
||||
|
|
Загрузка…
Ссылка в новой задаче