Bug 1300995 - Part 1. Add a footer on formautofill popup to let users open a preferences privacy tab when click on it. r=MattN

MozReview-Commit-ID: Izr6IbHlkLY

--HG--
extra : rebase_source : 435537913bc72143d7d4d6e4d28ac0b6192cc0e8
This commit is contained in:
Ray Lin 2017-06-01 21:53:37 +08:00
parent feb007373f
commit ea283e4f3d
16 changed files with 247 additions and 70 deletions

View File

@ -196,6 +196,10 @@ let ProfileAutocomplete = {
Services.obs.removeObserver(this, "autocomplete-will-enter-text");
},
getProfileAutoCompleteResult() {
return this._lastAutoCompleteResult;
},
setProfileAutoCompleteResult(result) {
this._lastAutoCompleteResult = result;
this._lastAutoCompleteFocusedInput = formFillController.focusedInput;
@ -502,8 +506,11 @@ var FormAutofillContent = {
_previewProfile(doc) {
let selectedIndex = ProfileAutocomplete._getSelectedIndex(doc.ownerGlobal);
let lastAutoCompleteResult = ProfileAutocomplete.getProfileAutoCompleteResult();
if (selectedIndex === -1) {
if (selectedIndex === -1 ||
!lastAutoCompleteResult ||
lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile") {
ProfileAutocomplete._clearProfilePreview();
} else {
ProfileAutocomplete._previewSelectedProfile(selectedIndex);
@ -517,6 +524,23 @@ var FormAutofillContent = {
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
},
_onKeyDown(e) {
let lastAutoCompleteResult = ProfileAutocomplete.getProfileAutoCompleteResult();
let focusedInput = formFillController.focusedInput;
if (e.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_RETURN || !lastAutoCompleteResult || !focusedInput) {
return;
}
let selectedIndex = ProfileAutocomplete._getSelectedIndex(e.target.ownerGlobal);
let selectedRowStyle = lastAutoCompleteResult.getStyleAt(selectedIndex);
if (selectedRowStyle == "autofill-footer") {
focusedInput.addEventListener("DOMAutoComplete", () => {
Services.cpmm.sendAsyncMessage("FormAutofill:OpenPreferences");
}, {once: true});
}
},
};

View File

@ -42,6 +42,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillPreferences",
"resource://formautofill/FormAutofillPreferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillDoorhanger",
"resource://formautofill/FormAutofillDoorhanger.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
"resource:///modules/RecentWindow.jsm");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
@ -81,6 +83,7 @@ FormAutofillParent.prototype = {
Services.ppmm.addMessageListener("FormAutofill:GetAddresses", this);
Services.ppmm.addMessageListener("FormAutofill:SaveAddress", this);
Services.ppmm.addMessageListener("FormAutofill:RemoveAddresses", this);
Services.ppmm.addMessageListener("FormAutofill:OpenPreferences", this);
Services.mm.addMessageListener("FormAutofill:OnFormSubmit", this);
// Observing the pref and storage changes
@ -196,6 +199,11 @@ FormAutofillParent.prototype = {
}
case "FormAutofill:OnFormSubmit": {
this._onFormSubmit(data, target);
break;
}
case "FormAutofill:OpenPreferences": {
const win = RecentWindow.getMostRecentBrowserWindow();
win.openPreferences("panePrivacy", {origin: "autofillFooter"});
}
}
},

View File

@ -37,6 +37,12 @@ this.ProfileAutoCompleteResult = function(searchString,
this._popupLabels = this._generateLabels(this._focusedFieldName,
this._allFieldNames,
this._matchingProfiles);
// Add an empty result entry for footer. Its content will come from
// the footer binding, so don't assign any value to it.
this._popupLabels.push({
primary: "",
secondary: "",
});
};
ProfileAutoCompleteResult.prototype = {
@ -179,6 +185,9 @@ ProfileAutoCompleteResult.prototype = {
*/
getStyleAt(index) {
this._checkIndexBounds(index);
if (index == this.matchCount - 1) {
return "autofill-footer";
}
return "autofill-profile";
},

View File

@ -27,6 +27,7 @@ var FormAutofillFrameScript = {
addEventListener("focusin", this);
addMessageListener("FormAutofill:PreviewProfile", this);
addMessageListener("FormAutoComplete:PopupClosed", this);
addMessageListener("FormAutoComplete:PopupOpened", this);
},
handleEvent(evt) {
@ -65,11 +66,24 @@ var FormAutofillFrameScript = {
return;
}
const doc = content.document;
const {chromeEventHandler} = doc.ownerGlobal.getInterface(Ci.nsIDocShell);
switch (message.name) {
case "FormAutofill:PreviewProfile":
case "FormAutoComplete:PopupClosed":
FormAutofillContent._previewProfile(content.document);
case "FormAutofill:PreviewProfile": {
FormAutofillContent._previewProfile(doc);
break;
}
case "FormAutoComplete:PopupClosed": {
FormAutofillContent._previewProfile(doc);
chromeEventHandler.removeEventListener("keydown", FormAutofillContent._onKeyDown,
{capturing: true});
break;
}
case "FormAutoComplete:PopupOpened": {
chromeEventHandler.addEventListener("keydown", FormAutofillContent._onKeyDown,
{capturing: true});
}
}
},
};

View File

@ -2,19 +2,27 @@
* 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/. */
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-profile"] {
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-profile"],
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-footer"] {
display: block;
margin: 0;
padding: 0;
height: auto;
min-height: auto;
}
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-profile"] {
-moz-binding: url("chrome://formautofill/content/formautofill.xml#autocomplete-profile-listitem");
}
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-footer"] {
-moz-binding: url("chrome://formautofill/content/formautofill.xml#autocomplete-profile-listitem-footer");
}
/* Treat @collpased="true" as display: none similar to how it is for XUL elements.
* https://developer.mozilla.org/en-US/docs/Web/CSS/visibility#Values */
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-profile"][collapsed="true"] {
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-profile"][collapsed="true"],
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-footer"][collapsed="true"] {
display: none;
}

View File

@ -9,14 +9,76 @@
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="autocomplete-profile-listitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<binding id="autocomplete-profile-listitem-base" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<resources>
<stylesheet src="chrome://formautofill-shared/skin/autocomplete-item.css"/>
<stylesheet src="chrome://formautofill/skin/autocomplete-item.css"/>
</resources>
<implementation implements="nsIDOMXULSelectControlItemElement">
<constructor>
</constructor>
<!-- For form autofill, we want to unify the selection no matter by
keyboard navigation or mouseover in order not to confuse user which
profile preview is being shown. This field is set to true to indicate
that selectedIndex of popup should be changed while mouseover item -->
<field name="selectedByMouseOver">true</field>
<property name="_stringBundle">
<getter><![CDATA[
/* global Services */
if (!this.__stringBundle) {
this.__stringBundle = Services.strings.createBundle("chrome://formautofill/locale/formautofill.properties");
}
return this.__stringBundle;
]]></getter>
</property>
<method name="_cleanup">
<body>
<![CDATA[
if (this._itemBox) {
this._itemBox.removeAttribute("size");
}
]]>
</body>
</method>
<method name="_onChanged">
<body></body>
</method>
<method name="_onOverflow">
<body></body>
</method>
<method name="_onUnderflow">
<body></body>
</method>
<method name="_adjustProfileItemLayout">
<body>
<![CDATA[
let outerBoxRect = this.parentNode.getBoundingClientRect();
// Make item fit in popup as XUL box could not constrain
// item's width
this._itemBox.style.width = outerBoxRect.width + "px";
// Use two-lines layout when width is smaller than 150px
if (outerBoxRect.width <= 150) {
this._itemBox.setAttribute("size", "small");
} else {
this._itemBox.removeAttribute("size");
}
]]>
</body>
</method>
</implementation>
</binding>
<binding id="autocomplete-profile-listitem" extends="chrome://formautofill/content/formautofill.xml#autocomplete-profile-listitem-base">
<xbl:content xmlns="http://www.w3.org/1999/xhtml">
<div anonid="profile-item-box" class="profile-item-box">
<div anonid="autofill-item-box" class="autofill-item-box">
<div class="profile-label-col profile-item-col">
<span anonid="profile-label" class="profile-label"></span>
</div>
@ -27,16 +89,10 @@
</xbl:content>
<implementation implements="nsIDOMXULSelectControlItemElement">
<!-- For form autofill, we want to unify the selection no matter by
keyboard navigation or mouseover in order not to confuse user which
profile preview is being shown. This field is set to true to indicate
that selectedIndex of popup should be changed while mouseover item -->
<field name="selectedByMouseOver">true</field>
<constructor>
<![CDATA[
this._itemBox = document.getAnonymousElementByAttribute(
this, "anonid", "profile-item-box"
this, "anonid", "autofill-item-box"
);
this._label = document.getAnonymousElementByAttribute(
this, "anonid", "profile-label"
@ -66,44 +122,60 @@
]]></setter>
</property>
<method name="_cleanup">
<body>
<![CDATA[
this._itemBox.removeAttribute("size");
]]>
</body>
</method>
<method name="_onChanged">
<body></body>
</method>
<method name="_onOverflow">
<body></body>
</method>
<method name="_onUnderflow">
<body></body>
</method>
<method name="_adjustAcItem">
<body>
<![CDATA[
let outerBoxRect = this.parentNode.getBoundingClientRect();
let {primary, secondary} = JSON.parse(this.getAttribute("ac-value"));
this._label.textContent = primary;
this._comment.textContent = secondary;
this._adjustProfileItemLayout();
]]>
</body>
</method>
</implementation>
</binding>
// Make item fit in popup as XUL box could not constrain
// item's width
this._itemBox.style.width = outerBoxRect.width + "px";
// Use two-lines layout when width is smaller than 150px
if (outerBoxRect.width <= 150) {
this._itemBox.setAttribute("size", "small");
} else {
this._itemBox.removeAttribute("size");
<binding id="autocomplete-profile-listitem-footer" extends="chrome://formautofill/content/formautofill.xml#autocomplete-profile-listitem-base">
<xbl:content xmlns="http://www.w3.org/1999/xhtml">
<div anonid="autofill-footer" class="autofill-item-box autofill-footer">
</div>
</xbl:content>
<handlers>
<handler event="click" button="0"><![CDATA[
window.openPreferences("panePrivacy", {origin: "autofillFooter"});
]]></handler>
</handlers>
<implementation implements="nsIDOMXULSelectControlItemElement">
<constructor>
<![CDATA[
this._itemBox = document.getAnonymousElementByAttribute(
this, "anonid", "autofill-footer"
);
this._adjustAcItem();
]]>
</constructor>
<method name="_adjustAcItem">
<body>
<![CDATA[
/* global Cu */
this._adjustProfileItemLayout();
let {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm", {});
let footerTextBundleKey = AppConstants.platform == "macosx" ?
"autocompleteFooterOptionOSX" : "autocompleteFooterOption";
// If the popup shows up with small layout, we should use short string to
// have a better fit in the box.
if (this._itemBox.getAttribute("size") == "small") {
footerTextBundleKey += "Short";
}
let footerText = this._stringBundle.GetStringFromName(footerTextBundleKey);
this._itemBox.textContent = footerText;
]]>
</body>
</method>

View File

@ -8,3 +8,7 @@ savedProfiles = Saved Profiles…
saveAddressMessage = Firefox now saves your form data to help you fill out forms faster!
viewAutofillOptions = View Form Autofill options…
openAutofillMessagePanel = Open Form Autofill message panel
autocompleteFooterOption = Form Autofill Options
autocompleteFooterOptionShort = Options
autocompleteFooterOptionOSX = Form Autofill Preferences
autocompleteFooterOptionOSXShort = Preferences

View File

@ -5,10 +5,14 @@
@namespace url("http://www.w3.org/1999/xhtml");
.profile-item-box > .profile-item-col > .profile-label {
.autofill-item-box > .profile-item-col > .profile-label {
font-size: .84em;
}
.profile-item-box > .profile-item-col > .profile-comment {
.autofill-item-box > .profile-item-col > .profile-comment {
font-size: .7em;
}
.autofill-footer {
font-size: .77em;
}

View File

@ -5,10 +5,10 @@
@namespace url("http://www.w3.org/1999/xhtml");
.profile-item-box > .profile-item-col > .profile-label {
.autofill-item-box > .profile-item-col > .profile-label {
font-size: 1.09em;
}
.profile-item-box > .profile-item-col > .profile-comment {
.autofill-item-box > .profile-item-col > .profile-comment {
font-size: .9em;
}

View File

@ -6,25 +6,35 @@
@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
xul|richlistitem[originaltype="autofill-profile"][selected="true"] > .profile-item-box {
xul|richlistitem[originaltype="autofill-profile"][selected="true"] > .autofill-item-box {
background-color: #F2F2F2;
}
.profile-item-box {
xul|richlistitem[originaltype="autofill-footer"][selected="true"] > .autofill-footer {
background-color: #DCDCDE;
}
.autofill-item-box {
--item-padding-vertical: 6px;
--item-padding-horizontal: 10px;
--col-spacer: 7px;
--item-width: calc(50% - (var(--col-spacer) / 2));
--item-text-color: -moz-FieldText;
}
.profile-item-box[size="small"] {
.autofill-item-box[size="small"] {
--item-padding-vertical: 7px;
--col-spacer: 0px;
--row-spacer: 3px;
--item-width: 100%;
}
.profile-item-box {
.autofill-footer {
--item-padding-vertical: 0;
--item-padding-horizontal: 0;
}
.autofill-item-box {
box-sizing: border-box;
margin: 0;
border-bottom: 1px solid rgba(38,38,38,.15);
@ -36,14 +46,14 @@ xul|richlistitem[originaltype="autofill-profile"][selected="true"] > .profile-it
flex-wrap: wrap;
align-items: center;
background-color: #FFFFFF;
color: -moz-FieldText
color: var(--item-text-color);
}
.profile-item-box:last-child {
.autofill-item-box:last-child {
border-bottom: 0;
}
.profile-item-box > .profile-item-col {
.autofill-item-box > .profile-item-col {
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
@ -51,21 +61,27 @@ xul|richlistitem[originaltype="autofill-profile"][selected="true"] > .profile-it
width: var(--item-width);
}
.profile-item-box > .profile-label-col {
.autofill-item-box > .profile-label-col {
text-align: start;
}
.profile-item-box > .profile-comment-col {
.autofill-item-box > .profile-comment-col {
margin-inline-start: var(--col-spacer);
text-align: end;
color: GrayText;
}
.profile-item-box[size="small"] {
.autofill-item-box[size="small"] {
flex-direction: column;
}
.profile-item-box[size="small"] > .profile-comment-col {
.autofill-item-box[size="small"] > .profile-comment-col {
margin-top: var(--row-spacer);
text-align: start;
}
.autofill-footer {
height: 41px;
background-color: #EDEDED;
justify-content: center;
}

View File

@ -6,12 +6,16 @@
@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
.profile-item-box > .profile-item-col > .profile-comment {
.autofill-item-box > .profile-item-col > .profile-comment {
font-size: .83em;
}
.autofill-footer {
font-size: .91em;
}
@media (-moz-windows-default-theme: 0) {
xul|richlistitem[originaltype="autofill-profile"][selected="true"] > .profile-item-box {
xul|richlistitem[originaltype="autofill-profile"][selected="true"] > .autofill-item-box {
background-color: Highlight;
}
}

View File

@ -43,10 +43,12 @@ async function onAddressChanged(type) {
});
}
function checkMenuEntries(expectedValues) {
function checkMenuEntries(expectedValues, isFormAutofillResult = true) {
let actualValues = getMenuEntries();
// Expect one more item would appear at the bottom as the footer if the result is from form autofill.
let expectedLength = isFormAutofillResult ? expectedValues.length + 1 : expectedValues.length;
is(actualValues.length, expectedValues.length, " Checking length of expected menu");
is(actualValues.length, expectedLength, " Checking length of expected menu");
for (let i = 0; i < expectedValues.length; i++) {
is(actualValues[i], expectedValues[i], " Checking menu entry #" + i);
}

View File

@ -107,7 +107,7 @@ add_task(async function history_only_menu_checking() {
await setInput("#tel", "");
doKey("down");
await expectPopup();
checkMenuEntries(["1-234-567-890"]);
checkMenuEntries(["1-234-567-890"], false);
});
// Form with both history and address storage.
@ -141,7 +141,7 @@ add_task(async function check_fallback_for_mismatched_field() {
await setInput("#email", "");
doKey("down");
await expectPopup();
checkMenuEntries(["foo@mozilla.com"]);
checkMenuEntries(["foo@mozilla.com"], false);
});
// Autofill the address from dropdown menu.
@ -161,7 +161,7 @@ add_task(async function check_fallback_after_form_autofill() {
await setInput("#tel", "");
doKey("down");
await expectPopup();
checkMenuEntries(["1-234-567-890"]);
checkMenuEntries(["1-234-567-890"], false);
});
registerPopupShownListener(popupShownListener);

View File

@ -123,6 +123,11 @@ add_task(async function check_preview() {
checkFormPreviewFields(MOCK_STORAGE[i]);
}
// Navigate to the footer
doKey("down");
await notifySelectedIndex(MOCK_STORAGE.length);
checkFormPreviewFields();
doKey("down");
await notifySelectedIndex(-1);
checkFormPreviewFields();

View File

@ -170,9 +170,16 @@ add_task(function* test_all_patterns() {
testCase.matchingProfiles,
testCase.options);
let expectedValue = testCase.expected;
let expectedItemLength = expectedValue.items.length;
// If the last item shows up as a footer, we expect one more item
// than expected.
if (actual.getStyleAt(actual.matchCount - 1) == "autofill-footer") {
expectedItemLength++;
}
equal(actual.searchResult, expectedValue.searchResult);
equal(actual.defaultIndex, expectedValue.defaultIndex);
equal(actual.matchCount, expectedValue.items.length);
equal(actual.matchCount, expectedItemLength);
expectedValue.items.forEach((item, index) => {
equal(actual.getValueAt(index), item.value);
equal(actual.getCommentAt(index), item.comment);
@ -182,13 +189,13 @@ add_task(function* test_all_patterns() {
});
if (expectedValue.items.length != 0) {
Assert.throws(() => actual.getValueAt(expectedValue.items.length),
Assert.throws(() => actual.getValueAt(expectedItemLength),
/Index out of range\./);
Assert.throws(() => actual.getLabelAt(expectedValue.items.length),
Assert.throws(() => actual.getLabelAt(expectedItemLength),
/Index out of range\./);
Assert.throws(() => actual.getCommentAt(expectedValue.items.length),
Assert.throws(() => actual.getCommentAt(expectedItemLength),
/Index out of range\./);
}
});

View File

@ -6433,7 +6433,7 @@
"alert_emails": ["jaws@mozilla.com"],
"expires_in_version": "59",
"kind": "categorical",
"labels": ["aboutHome", "aboutTelemetry", "browserMedia", "commandLine", "commandLineLegacy", "ContainersCommand", "contentSearch", "dataReporting", "doorhanger", "devDisconnectedAlert", "experimentsOpenPref", "fxa", "fxaSignedin", "fxaError", "offlineApps", "prefserviceDefaults", "preferencesButton", "paneSync", "storagePressure", "translationInfobar", "UITour", "menubar", "notifOpenSettings", "other"],
"labels": ["aboutHome", "aboutTelemetry", "browserMedia", "commandLine", "commandLineLegacy", "ContainersCommand", "contentSearch", "dataReporting", "doorhanger", "devDisconnectedAlert", "experimentsOpenPref", "fxa", "fxaSignedin", "fxaError", "offlineApps", "prefserviceDefaults", "preferencesButton", "paneSync", "storagePressure", "translationInfobar", "UITour", "menubar", "notifOpenSettings", "other", "autofillFooter"],
"releaseChannelCollection": "opt-out",
"description":"Count how the Preferences are opened."
},