Bug 1303510 - Part 1: Implement doorhanger helper and parameter for first time use doorhanger. r=lchang

MozReview-Commit-ID: 3LS5wEh6JlU

--HG--
extra : rebase_source : df5bb51658a8bb3ac1e08a9c30453c247147d650
This commit is contained in:
steveck-chung 2017-06-12 12:31:07 +08:00
Родитель 6848748f06
Коммит 006927acdf
7 изменённых файлов: 210 добавлений и 8 удалений

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

@ -1669,6 +1669,7 @@ pref("extensions.formautofill.experimental", true);
pref("extensions.formautofill.experimental", false);
#endif
pref("extensions.formautofill.addresses.enabled", true);
pref("extensions.formautofill.firstTimeUse", true);
pref("extensions.formautofill.heuristics.enabled", true);
pref("extensions.formautofill.loglevel", "Warn");

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

@ -324,9 +324,11 @@ var FormAutofillContent = {
* Send the profile to parent for doorhanger and storage saving/updating.
*
* @param {Object} profile Submitted form's address/creditcard guid and record.
* @param {Object} domWin Current content window.
*/
_onFormSubmit(profile) {
Services.cpmm.sendAsyncMessage("FormAutofill:OnFormSubmit", profile);
_onFormSubmit(profile, domWin) {
let mm = this._messageManagerFromWindow(domWin);
mm.sendAsyncMessage("FormAutofill:OnFormSubmit", profile);
},
/**
@ -365,7 +367,7 @@ var FormAutofillContent = {
record: pendingAddress,
},
// creditCard: {}
});
}, domWin);
return true;
},
@ -507,6 +509,14 @@ var FormAutofillContent = {
ProfileAutocomplete._previewSelectedProfile(selectedIndex);
}
},
_messageManagerFromWindow(win) {
return win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
},
};

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

@ -0,0 +1,173 @@
/* 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 doorhanger singleton that wraps up the PopupNotifications and handles
* the doorhager UI for formautofill related features.
*/
/* exported FormAutofillDoorhanger */
"use strict";
this.EXPORTED_SYMBOLS = ["FormAutofillDoorhanger"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
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, this.EXPORTED_SYMBOLS[0]);
const BUNDLE_URI = "chrome://formautofill/locale/formautofill.properties";
const GetStringFromName = Services.strings.createBundle(BUNDLE_URI).GetStringFromName;
const CONTENT = {
firstTimeUse: {
notificationId: "autofill-address",
message: GetStringFromName("saveAddressMessage"),
anchor: {
id: "autofill-address-notification-icon",
URL: "chrome://formautofill/content/icon-address-save.svg",
tooltiptext: GetStringFromName("openAutofillMessagePanel"),
},
options: {
persistWhileVisible: true,
popupIconURL: "chrome://formautofill/content/icon-address-save.svg",
},
},
};
let FormAutofillDoorhanger = {
/**
* Generate the main action and secondary actions from content parameters and
* promise resolve.
*
* @private
* @param {Object} mainActionParams
* Parameters for main action.
* @param {Array<Object>} secondaryActionParams
* Array of the parameters for secondary actions.
* @param {Function} resolve Should be called in action callback.
* @returns {Array<Object>}
Return the mainAction and secondary actions in an array for showing doorhanger
*/
_createActions(mainActionParams, secondaryActionParams, resolve) {
if (!mainActionParams) {
return [null, null];
}
let {label, accessKey, callbackState} = mainActionParams;
let callback = resolve.bind(null, callbackState);
let mainAction = {label, accessKey, callback};
if (!secondaryActionParams) {
return [mainAction, null];
}
let secondaryActions = [];
for (let params of secondaryActionParams) {
let cb = resolve.bind(null, params.callbackState);
secondaryActions.push({
label: params.label,
accessKey: params.accessKey,
callback: cb,
});
}
return [mainAction, secondaryActions];
},
/**
* Append the link label element to the popupnotificationcontent.
* @param {XULElement} browser
* Target browser element for showing doorhanger.
* @param {string} id
* The ID of the doorhanger.
*/
_appendPrivacyPanelLink(browser, id) {
let notificationId = id + "-notification";
let chromeDoc = browser.ownerDocument;
let notification = chromeDoc.getElementById(notificationId);
if (!notification.querySelector("popupnotificationcontent")) {
let notificationcontent = chromeDoc.createElement("popupnotificationcontent");
let privacyLinkElement = chromeDoc.createElement("label");
privacyLinkElement.className = "text-link";
privacyLinkElement.setAttribute("useoriginprincipal", true);
privacyLinkElement.setAttribute("href", "about:preferences#privacy");
privacyLinkElement.setAttribute("value", GetStringFromName("viewAutofillOptions"));
notificationcontent.appendChild(privacyLinkElement);
notification.append(notificationcontent);
}
},
/**
* Create an image element for notification anchor if it doesn't already exist.
* @param {XULElement} browser
* Target browser element for showing doorhanger.
* @param {Object} anchor
* Anchor options for setting the anchor element.
* @param {string} anchor.id
* ID of the anchor element.
* @param {string} anchor.URL
* Path of the icon asset.
* @param {string} anchor.tooltiptext
* Tooltip string for the anchor.
*/
_setAnchor(browser, anchor) {
let chromeDoc = browser.ownerDocument;
let {id, URL, tooltiptext} = anchor;
let anchorEt = chromeDoc.getElementById(id);
if (!anchorEt) {
let notificationPopupBox =
chromeDoc.getElementById("notification-popup-box");
// Icon shown on URL bar
let anchorElement = chromeDoc.createElement("image");
anchorElement.id = id;
anchorElement.setAttribute("src", URL);
anchorElement.classList.add("notification-anchor-icon");
anchorElement.setAttribute("role", "button");
anchorElement.setAttribute("tooltiptext", tooltiptext);
anchorElement.style.setProperty("-moz-context-properties", "fill");
anchorElement.style.fill = "currentcolor";
notificationPopupBox.appendChild(anchorElement);
}
},
/**
* Show different types of doorhanger by leveraging PopupNotifications.
* @param {XULElement} browser
* Target browser element for showing doorhanger.
* @param {string} type
* The type of the doorhanger. There will have first time use/update/credit card.
* @returns {Promise}
Resolved with action type when action callback is triggered.
*/
async show(browser, type) {
log.debug("show doorhanger with type:", type);
return new Promise((resolve) => {
let content = CONTENT[type];
let chromeWin = browser.ownerGlobal;
content.options.eventCallback = (topic) => {
log.debug("eventCallback:", topic);
switch (topic) {
// We can only append label element when notification box is shown
case "shown":
this._appendPrivacyPanelLink(browser, content.notificationId);
break;
}
};
this._setAnchor(browser, content.anchor);
chromeWin.PopupNotifications.show(
browser,
content.notificationId,
content.message,
content.anchor.id,
...this._createActions(content.mainAction, content.secondaryActions, resolve),
content.options,
);
});
},
};

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

@ -35,12 +35,13 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillPreferences",
"resource://formautofill/FormAutofillPreferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillDoorhanger",
"resource://formautofill/FormAutofillDoorhanger.jsm");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
@ -80,7 +81,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:OnFormSubmit", this);
Services.mm.addMessageListener("FormAutofill:OnFormSubmit", this);
// Observing the pref and storage changes
Services.prefs.addObserver(ENABLED_PREF, this);
@ -276,8 +277,16 @@ FormAutofillParent.prototype = {
}
this.profileStorage.addresses.notifyUsed(address.guid);
} else {
// TODO: Add first time use probe(bug 990199) and doorhanger(bug 1303510)
// profileStorage.addresses.add(address.record);
if (!Services.prefs.getBoolPref("extensions.formautofill.firstTimeUse")) {
if (!this.profileStorage.addresses.mergeToStorage(address.record)) {
this.profileStorage.addresses.add(address.record);
}
return;
}
this.profileStorage.addresses.add(address.record);
Services.prefs.setBoolPref("extensions.formautofill.firstTimeUse", false);
FormAutofillDoorhanger.show(target, "firstTimeUse");
}
},
};

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

@ -0,0 +1,6 @@
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path fill="#999899" d="M22 13.7H9.4c-.6 0-1.2.5-1.2 1.2 0 .6.5 1.2 1.2 1.2H22c.6 0 1.2-.5 1.2-1.2s-.6-1.2-1.2-1.2zM6.1 26.6V5.5c0-.8.7-1.5 1.5-1.5h16c.9 0 1.5.6 1.5 1.5V16h2V3.8c0-1-.7-1.8-1.8-1.8H5.9c-1 0-1.8.8-1.8 1.8v24.5c0 1 .8 1.7 1.8 1.7h9.3v-2H7.6c-.8 0-1.5-.6-1.5-1.4zm21.1-1.9h-2.5V20c0-.4-.3-.8-.8-.8h-3.1c-.4 0-.8.3-.8.8v4.6h-2.5c-.6 0-.8.4-.3.8l4.3 4.2c.2.2.5.3.8.3s.6-.1.8-.3l4.3-4.2c.6-.4.4-.7-.2-.7zm-11.3-5.6H9.4c-.6 0-1.2.5-1.2 1.2s.5 1.2 1.2 1.2h6.5c.6 0 1.2-.5 1.2-1.2s-.6-1.2-1.2-1.2zM22 7.8H9.4c-.6 0-1.2.5-1.2 1.2s.5 1.2 1.2 1.2H22c.6 0 1.2-.5 1.2-1.2s-.6-1.2-1.2-1.2z"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 877 B

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

@ -5,3 +5,6 @@
preferenceGroupTitle = Form Autofill
enableProfileAutofill = Enable Profile Autofill
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

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

@ -108,7 +108,7 @@ TESTCASES.forEach(testcase => {
let input = MOCK_DOC.getElementById(key);
input.value = testcase.formValue[key];
}
sinon.spy(FormAutofillContent, "_onFormSubmit");
sinon.stub(FormAutofillContent, "_onFormSubmit");
FormAutofillContent.identifyAutofillFields(MOCK_DOC);
FormAutofillContent.notify(form);