blob: 494dc0e09af26613ddceb3ba06ef61a4c46268c8 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview 'settings-autofill-section' is the section containing saved
* addresses for use in autofill and payments APIs.
*/
import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.m.js';
import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
import 'chrome://resources/cr_elements/shared_style_css.m.js';
import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
import '../settings_shared_css.js';
import '../controls/extension_controlled_indicator.js';
import '../controls/settings_toggle_button.js';
import '../prefs/prefs.js';
import './address_edit_dialog.js';
import './address_remove_confirmation_dialog.js';
import './passwords_shared_css.js';
import {assert} from 'chrome://resources/js/assert.m.js';
import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
import {focusWithoutInk} from 'chrome://resources/js/cr/ui/focus_without_ink.m.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {loadTimeData} from '../i18n_setup.js';
/** @typedef {chrome.autofillPrivate.CreditCardEntry} */
let CreditCardEntry;
/**
* Interface for all callbacks to the autofill API.
* @interface
*/
export class AutofillManager {
/**
* Add an observer to the list of personal data.
* @param {function(!Array<!AutofillManager.AddressEntry>,
* !Array<!CreditCardEntry>):void} listener
*/
setPersonalDataManagerListener(listener) {}
/**
* Remove an observer from the list of personal data.
* @param {function(!Array<!AutofillManager.AddressEntry>,
* !Array<!CreditCardEntry>):void} listener
*/
removePersonalDataManagerListener(listener) {}
/**
* Request the list of addresses.
* @param {function(!Array<!AutofillManager.AddressEntry>):void}
* callback
*/
getAddressList(callback) {}
/**
* Saves the given address.
* @param {!AutofillManager.AddressEntry} address
*/
saveAddress(address) {}
/** @param {string} guid The guid of the address to remove. */
removeAddress(guid) {}
}
/** @typedef {chrome.autofillPrivate.AddressEntry} */
AutofillManager.AddressEntry;
/**
* Implementation that accesses the private API.
* @implements {AutofillManager}
*/
export class AutofillManagerImpl {
/** @override */
setPersonalDataManagerListener(listener) {
chrome.autofillPrivate.onPersonalDataChanged.addListener(listener);
}
/** @override */
removePersonalDataManagerListener(listener) {
chrome.autofillPrivate.onPersonalDataChanged.removeListener(listener);
}
/** @override */
getAddressList(callback) {
chrome.autofillPrivate.getAddressList(callback);
}
/** @override */
saveAddress(address) {
chrome.autofillPrivate.saveAddress(address);
}
/** @override */
removeAddress(guid) {
chrome.autofillPrivate.removeEntry(assert(guid));
}
}
addSingletonGetter(AutofillManagerImpl);
Polymer({
is: 'settings-autofill-section',
_template: html`{__html_template__}`,
properties: {
/**
* An array of saved addresses.
* @type {!Array<!AutofillManager.AddressEntry>}
*/
addresses: Array,
/**
* The model for any address related action menus or dialogs.
* @private {?chrome.autofillPrivate.AddressEntry}
*/
activeAddress: Object,
/** @private */
showAddressDialog_: Boolean,
/** @private */
showAddressRemoveConfirmationDialog_: Boolean,
},
listeners: {
'save-address': 'saveAddress_',
},
/**
* The element to return focus to, when the currently active dialog is
* closed.
* @private {?HTMLElement}
*/
activeDialogAnchor_: null,
/**
* @type {AutofillManager}
* @private
*/
autofillManager_: null,
/**
* @type {?function(!Array<!AutofillManager.AddressEntry>,
* !Array<!CreditCardEntry>)}
* @private
*/
setPersonalDataListener_: null,
/** @override */
attached() {
// Create listener functions.
/** @type {function(!Array<!AutofillManager.AddressEntry>)} */
const setAddressesListener = addressList => {
this.addresses = addressList;
};
/**
* @type {function(!Array<!AutofillManager.AddressEntry>,
* !Array<!CreditCardEntry>)}
*/
const setPersonalDataListener = (addressList, cardList) => {
this.addresses = addressList;
};
// Remember the bound reference in order to detach.
this.setPersonalDataListener_ = setPersonalDataListener;
// Set the managers. These can be overridden by tests.
this.autofillManager_ = AutofillManagerImpl.getInstance();
// Request initial data.
this.autofillManager_.getAddressList(setAddressesListener);
// Listen for changes.
this.autofillManager_.setPersonalDataManagerListener(
setPersonalDataListener);
// Record that the user opened the address settings.
chrome.metricsPrivate.recordUserAction('AutofillAddressesViewed');
},
/** @override */
detached() {
this.autofillManager_.removePersonalDataManagerListener(
/**
@type {function(!Array<!AutofillManager.AddressEntry>,
!Array<!CreditCardEntry>)}
*/
(this.setPersonalDataListener_));
},
/**
* Open the address action menu.
* @param {!Event} e The polymer event.
* @private
*/
onAddressMenuTap_(e) {
const menuEvent = /** @type {!{model: !{item: !Object}}} */ (e);
const item = menuEvent.model.item;
// Copy item so dialog won't update model on cancel.
this.activeAddress = /** @type {!chrome.autofillPrivate.AddressEntry} */ (
Object.assign({}, item));
const dotsButton = /** @type {!HTMLElement} */ (e.target);
/** @type {!CrActionMenuElement} */ (this.$.addressSharedMenu)
.showAt(dotsButton);
this.activeDialogAnchor_ = dotsButton;
},
/**
* Handles tapping on the "Add address" button.
* @param {!Event} e The polymer event.
* @private
*/
onAddAddressTap_(e) {
e.preventDefault();
this.activeAddress = {};
this.showAddressDialog_ = true;
this.activeDialogAnchor_ = /** @type {HTMLElement} */ (this.$.addAddress);
},
/** @private */
onAddressDialogClose_() {
this.showAddressDialog_ = false;
focusWithoutInk(assert(this.activeDialogAnchor_));
this.activeDialogAnchor_ = null;
},
/**
* Handles tapping on the "Edit" address button.
* @param {!Event} e The polymer event.
* @private
*/
onMenuEditAddressTap_(e) {
e.preventDefault();
this.showAddressDialog_ = true;
this.$.addressSharedMenu.close();
},
/** @private */
onAddressRemoveConfirmationDialogClose_: function() {
// Check if the dialog was confirmed before closing it.
if (/** @type {!SettingsAddressRemoveConfirmationDialogElement} */
(this.$$('settings-address-remove-confirmation-dialog'))
.wasConfirmed()) {
this.autofillManager_.removeAddress(
/** @type {string} */ (this.activeAddress.guid));
}
this.showAddressRemoveConfirmationDialog_ = false;
focusWithoutInk(assert(this.activeDialogAnchor_));
this.activeDialogAnchor_ = null;
},
/**
* Handles tapping on the "Remove" address button.
* @private
*/
onMenuRemoveAddressTap_() {
this.showAddressRemoveConfirmationDialog_ = true;
this.$.addressSharedMenu.close();
},
/**
* Returns true if the list exists and has items.
* @param {Array<Object>} list
* @return {boolean}
* @private
*/
hasSome_(list) {
return !!(list && list.length);
},
/**
* Listens for the save-address event, and calls the private API.
* @param {!Event} event
* @private
*/
saveAddress_(event) {
this.autofillManager_.saveAddress(event.detail);
},
});