summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrouslan@chromium.org <rouslan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-04 05:13:57 +0000
committerrouslan@chromium.org <rouslan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-04 05:13:57 +0000
commitf80cec86d2eac6ac9730615c093d369b120a678d (patch)
tree998a47d220296d2c1be9285892a0aa70bd347e45
parent699232a42be203a95c97a7153f30f6da176ca1f5 (diff)
downloadchromium_src-f80cec86d2eac6ac9730615c093d369b120a678d.zip
chromium_src-f80cec86d2eac6ac9730615c093d369b120a678d.tar.gz
chromium_src-f80cec86d2eac6ac9730615c093d369b120a678d.tar.bz2
i18n address editing in chrome://settings/autofillEditAddress.
BUG= 333387 Review URL: https://codereview.chromium.org/243013004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268095 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/generated_resources.grd31
-rw-r--r--chrome/browser/resources/options/autofill_edit_address_overlay.html48
-rw-r--r--chrome/browser/resources/options/autofill_edit_address_overlay.js326
-rw-r--r--chrome/browser/resources/options/autofill_edit_overlay.css47
-rw-r--r--chrome/browser/resources/options/autofill_options_list.js3
-rw-r--r--chrome/browser/ui/webui/DEPS1
-rw-r--r--chrome/browser/ui/webui/options/autofill_options_browsertest.js118
-rw-r--r--chrome/browser/ui/webui/options/autofill_options_handler.cc304
-rw-r--r--chrome/browser/ui/webui/options/autofill_options_handler.h5
9 files changed, 545 insertions, 338 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 31b906b..aa0c82a 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10039,41 +10039,14 @@ Would you like to start <ph name="CONTROL_PANEL_APPLET_NAME">$1<ex>Add/Remove Pr
Add a credit card
</message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_FIRST_NAME" desc="The label of the first name entry.">
- First name
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_MIDDLE_NAME" desc="The label of the middle name entry.">
- Middle name(s)
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_LAST_NAME" desc="The label of the last name entry.">
- Last name
- </message>
<message name="IDS_AUTOFILL_FIELD_LABEL_EMAIL" desc="The label of the Email entry.">
Email
</message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_COMPANY_NAME" desc="The label of the Company name entry.">
- Company name
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_ADDRESS_LINE_1" desc="The label of the Address Line 1 entry.">
- Address line 1
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_ADDRESS_LINE_2" desc="The label of the Address Line 2 entry.">
- Address line 2
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_CITY" desc="The label of the City entry.">
- City/Town
- </message>
<message name="IDS_AUTOFILL_FIELD_LABEL_PHONE" desc="The label of the Phone entry.">
Phone
</message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_ADD_FIRST_NAME" desc="The placeholder text of the first name field.">
- Add first name
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_ADD_MIDDLE_NAME" desc="The placeholder text of the middle name field.">
- Add middle name(s)
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_ADD_LAST_NAME" desc="The placeholder text of the last name field.">
- Add last name
+ <message name="IDS_AUTOFILL_FIELD_LABEL_ADD_NAME" desc="The placeholder text of the recipient or contact name field.">
+ Add name
</message>
<message name="IDS_AUTOFILL_FIELD_LABEL_ADD_PHONE" desc="The placeholder text of the phone field.">
Add phone number
diff --git a/chrome/browser/resources/options/autofill_edit_address_overlay.html b/chrome/browser/resources/options/autofill_edit_address_overlay.html
index a54bc85..3b7ef00 100644
--- a/chrome/browser/resources/options/autofill_edit_address_overlay.html
+++ b/chrome/browser/resources/options/autofill_edit_address_overlay.html
@@ -2,66 +2,26 @@
<div class="close-button"></div>
<h1 id="autofill-address-title"></h1>
<div class="content-area">
- <div>
- <div id="autofill-name-labels">
- <span i18n-content="autofillFirstNameLabel"></span>
- <span i18n-content="autofillMiddleNameLabel"></span>
- <span i18n-content="autofillLastNameLabel"></span>
- </div>
- </div>
- <div>
- <list id="full-name-list"></list>
- </div>
-
- <label class="settings-row">
- <div i18n-content="autofillCompanyNameLabel"></div>
- <input id="company-name" type="text">
- </label>
-
- <label class="settings-row">
- <div i18n-content="autofillAddrLine1Label"></div>
- <input id="addr-line-1" type="text">
- </label>
-
- <label class="settings-row">
- <div i18n-content="autofillAddrLine2Label"></div>
- <input id="addr-line-2" type="text">
- </label>
-
- <div class="input-group settings-row">
- <label>
- <div i18n-content="autofillCityLabel"></div>
- <input id="city" type="text">
- </label>
-
- <label>
- <div id="state-label"></div>
- <input id="state" type="text">
- </label>
-
- <label>
- <div id="postal-code-label"></div>
- <input id="postal-code" type="text">
- </label>
+ <div id="autofill-edit-address-fields">
</div>
<div class="settings-row">
<label>
<div i18n-content="autofillCountryLabel"></div>
- <select id="country"></select>
+ <select class="country" field="country"></select>
</label>
</div>
<div class="input-group settings-row">
<div>
<div i18n-content="autofillPhoneLabel"></div>
- <list id="phone-list"
+ <list class="short" field="phone"
i18n-values="placeholder:autofillAddPhonePlaceholder"></list>
</div>
<div>
<div i18n-content="autofillEmailLabel"></div>
- <list id="email-list"
+ <list class="short" field="email"
i18n-values="placeholder:autofillAddEmailPlaceholder"></list>
</div>
</div>
diff --git a/chrome/browser/resources/options/autofill_edit_address_overlay.js b/chrome/browser/resources/options/autofill_edit_address_overlay.js
index 1f1bf5b..c5e9f25 100644
--- a/chrome/browser/resources/options/autofill_edit_address_overlay.js
+++ b/chrome/browser/resources/options/autofill_edit_address_overlay.js
@@ -9,6 +9,9 @@ cr.define('options', function() {
// The GUID of the loaded address.
var guid;
+ // The BCP 47 language code for the layout of input fields.
+ var languageCode;
+
/**
* AutofillEditAddressOverlay class
* Encapsulated handling of the 'Add Page' overlay page.
@@ -46,10 +49,11 @@ cr.define('options', function() {
// Blurring is delayed for list elements. Queue save and close to
// ensure that pending changes have been applied.
setTimeout(function() {
- $('phone-list').doneValidating().then(function() {
- self.saveAddress_();
- self.dismissOverlay_();
- });
+ self.pageDiv.querySelector('[field=phone]').doneValidating().then(
+ function() {
+ self.saveAddress_();
+ self.dismissOverlay_();
+ });
}, 0);
};
@@ -64,10 +68,17 @@ cr.define('options', function() {
event.preventDefault();
};
- self.guid = '';
- self.populateCountryList_();
- self.clearInputFields_();
- self.connectInputEvents_();
+ this.guid = '';
+ this.populateCountryList_();
+ this.rebuildInputFields_(
+ loadTimeData.getValue('autofillDefaultCountryComponents'));
+ this.languageCode =
+ loadTimeData.getString('autofillDefaultCountryLanguageCode');
+ this.connectInputEvents_();
+ this.setInputFields_({});
+ this.getCountrySelector_().onchange = function(event) {
+ self.countryChanged_();
+ };
},
/**
@@ -81,34 +92,27 @@ cr.define('options', function() {
},
/**
- * Creates, decorates and initializes the multi-value lists for full name,
- * phone, and email.
+ * Creates, decorates and initializes the multi-value lists for phone and
+ * email.
* @private
*/
createMultiValueLists_: function() {
- var list = $('full-name-list');
- options.autofillOptions.AutofillNameValuesList.decorate(list);
- list.autoExpands = true;
-
- list = $('phone-list');
+ var list = this.pageDiv.querySelector('[field=phone]');
options.autofillOptions.AutofillPhoneValuesList.decorate(list);
list.autoExpands = true;
- list = $('email-list');
+ list = this.pageDiv.querySelector('[field=email]');
options.autofillOptions.AutofillValuesList.decorate(list);
list.autoExpands = true;
},
/**
- * Updates the data model for the list named |listName| with the values from
- * |entries|.
- * @param {string} listName The id of the list.
+ * Updates the data model for the |list| with the values from |entries|.
+ * @param {element} list The list to update.
* @param {Array} entries The list of items to be added to the list.
+ * @private
*/
- setMultiValueList_: function(listName, entries) {
- // Add data entries.
- var list = $(listName);
-
+ setMultiValueList_: function(list, entries) {
// Add special entry for adding new values.
var augmentedList = entries.slice();
augmentedList.push(null);
@@ -129,32 +133,106 @@ cr.define('options', function() {
* @private
*/
dismissOverlay_: function() {
- this.clearInputFields_();
+ this.setInputFields_({});
+ this.inputFieldChanged_();
this.guid = '';
+ this.languageCode = '';
OptionsPage.closeOverlay();
},
/**
+ * Returns the country selector element.
+ * @return {element} The country selector.
+ * @private
+ */
+ getCountrySelector_: function() {
+ return this.pageDiv.querySelector('[field=country]');
+ },
+
+ /**
+ * Returns all list elements.
+ * @return {NodeList} The list elements.
+ * @private
+ */
+ getLists_: function() {
+ return this.pageDiv.querySelectorAll('list[field]');
+ },
+
+ /**
+ * Returns all text input elements.
+ * @return {NodeList} The text input elements.
+ * @private
+ */
+ getTextFields_: function() {
+ return this.pageDiv.querySelectorAll(
+ ':-webkit-any(textarea, input)[field]');
+ },
+
+ /**
+ * Aggregates the values in the input fields into an object.
+ * @return {object} The mapping from field names to values.
+ * @private
+ */
+ getInputFields_: function() {
+ var address = {};
+ address['country'] = this.getCountrySelector_().value;
+
+ var lists = this.getLists_();
+ for (var i = 0; i < lists.length; i++) {
+ address[lists[i].getAttribute('field')] =
+ lists[i].dataModel.slice(0, lists[i].dataModel.length - 1);
+ }
+
+ var fields = this.getTextFields_();
+ for (var i = 0; i < fields.length; i++) {
+ address[fields[i].getAttribute('field')] = fields[i].value;
+ }
+
+ return address;
+ },
+
+ /**
+ * Sets the value of each input field according to |address|.
+ * @param {object} address The object with values to use.
+ * @private
+ */
+ setInputFields_: function(address) {
+ this.getCountrySelector_().value = address['country'] || '';
+
+ var lists = this.getLists_();
+ for (var i = 0; i < lists.length; i++) {
+ this.setMultiValueList_(
+ lists[i], address[lists[i].getAttribute('field')] || []);
+ }
+
+ var fields = this.getTextFields_();
+ for (var i = 0; i < fields.length; i++) {
+ fields[i].value = address[fields[i].getAttribute('field')] || '';
+ }
+ },
+
+ /**
* Aggregates the values in the input fields into an array and sends the
* array to the Autofill handler.
* @private
*/
saveAddress_: function() {
+ var inputFields = this.getInputFields_();
var address = new Array();
- address[0] = this.guid;
- var list = $('full-name-list');
- address[1] = list.dataModel.slice(0, list.dataModel.length - 1);
- address[2] = $('company-name').value;
- address[3] = $('addr-line-1').value;
- address[4] = $('addr-line-2').value;
- address[5] = $('city').value;
- address[6] = $('state').value;
- address[7] = $('postal-code').value;
- address[8] = $('country').value;
- list = $('phone-list');
- address[9] = list.dataModel.slice(0, list.dataModel.length - 1);
- list = $('email-list');
- address[10] = list.dataModel.slice(0, list.dataModel.length - 1);
+ var argCounter = 0;
+ address[argCounter++] = this.guid;
+ address[argCounter++] = inputFields['fullName'] || [];
+ address[argCounter++] = inputFields['companyName'] || '';
+ address[argCounter++] = inputFields['addrLines'] || '';
+ address[argCounter++] = inputFields['dependentLocality'] || '';
+ address[argCounter++] = inputFields['city'] || '';
+ address[argCounter++] = inputFields['state'] || '';
+ address[argCounter++] = inputFields['postalCode'] || '';
+ address[argCounter++] = inputFields['sortingCode'] || '';
+ address[argCounter++] = inputFields['country'] || '';
+ address[argCounter++] = inputFields['phone'] || [];
+ address[argCounter++] = inputFields['email'] || [];
+ address[argCounter++] = this.languageCode;
chrome.send('setAddress', address);
},
@@ -167,51 +245,56 @@ cr.define('options', function() {
*/
connectInputEvents_: function() {
var self = this;
- $('company-name').oninput = $('addr-line-1').oninput =
- $('addr-line-2').oninput = $('city').oninput = $('state').oninput =
- $('postal-code').oninput = function(event) {
- self.inputFieldChanged_();
- };
-
- $('country').onchange = function(event) {
- self.countryChanged_();
- };
+ var fields = this.getTextFields_();
+ for (var i = 0; i < fields.length; i++) {
+ fields[i].oninput = function(event) { self.inputFieldChanged_(); };
+ }
},
/**
- * Checks the values of each of the input fields and disables the 'Ok'
- * button if all of the fields are empty.
+ * Disables the 'Ok' button if all of the fields are empty.
* @private
*/
inputFieldChanged_: function() {
- // Length of lists are tested for <= 1 due to the "add" placeholder item
- // in the list.
- var disabled =
- $('full-name-list').items.length <= 1 &&
- !$('company-name').value &&
- !$('addr-line-1').value && !$('addr-line-2').value &&
- !$('city').value && !$('state').value && !$('postal-code').value &&
- !$('country').value && $('phone-list').items.length <= 1 &&
- $('email-list').items.length <= 1;
+ var disabled = true;
+ if (this.getCountrySelector_().value)
+ disabled = false;
+
+ if (disabled) {
+ // Length of lists are tested for > 1 due to the "add" placeholder item
+ // in the list.
+ var lists = this.getLists_();
+ for (var i = 0; i < lists.length; i++) {
+ if (lists[i].items.length > 1) {
+ disabled = false;
+ break;
+ }
+ }
+ }
+
+ if (disabled) {
+ var fields = this.getTextFields_();
+ for (var i = 0; i < fields.length; i++) {
+ if (fields[i].value) {
+ disabled = false;
+ break;
+ }
+ }
+ }
+
$('autofill-edit-address-apply-button').disabled = disabled;
},
/**
- * Updates the postal code and state field labels appropriately for the
- * selected country.
+ * Updates the address fields appropriately for the selected country.
* @private
*/
countryChanged_: function() {
- var countryCode = $('country').value ||
- loadTimeData.getString('defaultCountryCode');
-
- var details = loadTimeData.getValue('autofillCountryData')[countryCode];
- var postal = $('postal-code-label');
- postal.textContent = details.postalCodeLabel;
- $('state-label').textContent = details.stateLabel;
-
- // Also update the 'Ok' button as needed.
- this.inputFieldChanged_();
+ var countryCode = this.getCountrySelector_().value;
+ if (countryCode)
+ chrome.send('loadAddressEditorComponents', [countryCode]);
+ else
+ this.inputFieldChanged_();
},
/**
@@ -222,7 +305,7 @@ cr.define('options', function() {
var countryList = loadTimeData.getValue('autofillCountrySelectList');
// Add the countries to the country <select> list.
- var countrySelect = $('country');
+ var countrySelect = this.getCountrySelector_();
// Add an empty option.
countrySelect.appendChild(new Option('', ''));
for (var i = 0; i < countryList.length; i++) {
@@ -234,52 +317,78 @@ cr.define('options', function() {
},
/**
- * Clears the value of each input field.
+ * Loads the address data from |address|, sets the input fields based on
+ * this data, and stores the GUID and language code of the address.
* @private
*/
- clearInputFields_: function() {
- this.setMultiValueList_('full-name-list', []);
- $('company-name').value = '';
- $('addr-line-1').value = '';
- $('addr-line-2').value = '';
- $('city').value = '';
- $('state').value = '';
- $('postal-code').value = '';
- $('country').value = '';
- this.setMultiValueList_('phone-list', []);
- this.setMultiValueList_('email-list', []);
-
- this.countryChanged_();
+ loadAddress_: function(address) {
+ this.rebuildInputFields_(address.components);
+ this.setInputFields_(address);
+ this.inputFieldChanged_();
+ this.connectInputEvents_();
+ this.guid = address.guid;
+ this.languageCode = address.languageCode;
},
/**
- * Loads the address data from |address|, sets the input fields based on
- * this data and stores the GUID of the address.
+ * Takes a snapshot of the input values, clears the input values, loads the
+ * address input layout from |input.components|, restores the input values
+ * from snapshot, and stores the |input.languageCode| for the address.
* @private
*/
- loadAddress_: function(address) {
+ loadAddressComponents_: function(input) {
+ var address = this.getInputFields_();
+ this.rebuildInputFields_(input.components);
this.setInputFields_(address);
this.inputFieldChanged_();
- this.guid = address.guid;
+ this.connectInputEvents_();
+ this.languageCode = input.languageCode;
},
/**
- * Sets the value of each input field according to |address|
+ * Clears address inputs and rebuilds the input fields according to
+ * |components|.
* @private
*/
- setInputFields_: function(address) {
- this.setMultiValueList_('full-name-list', address.fullName);
- $('company-name').value = address.companyName;
- $('addr-line-1').value = address.addrLine1;
- $('addr-line-2').value = address.addrLine2;
- $('city').value = address.city;
- $('state').value = address.state;
- $('postal-code').value = address.postalCode;
- $('country').value = address.country;
- this.setMultiValueList_('phone-list', address.phone);
- this.setMultiValueList_('email-list', address.email);
-
- this.countryChanged_();
+ rebuildInputFields_: function(components) {
+ var content = $('autofill-edit-address-fields');
+ while (content.firstChild) {
+ content.removeChild(content.firstChild);
+ }
+
+ var customContainerElements = {'fullName': 'div'};
+ var customInputElements = {'fullName': 'list', 'addrLines': 'textarea'};
+
+ for (var i in components) {
+ var row = document.createElement('div');
+ row.classList.add('input-group', 'settings-row');
+ content.appendChild(row);
+
+ for (var j in components[i]) {
+ if (components[i][j].field == 'country')
+ continue;
+
+ var fieldContainer = document.createElement(
+ customContainerElements[components[i][j].field] || 'label');
+ row.appendChild(fieldContainer);
+
+ var fieldName = document.createElement('div');
+ fieldName.textContent = components[i][j].name;
+ fieldContainer.appendChild(fieldName);
+
+ var input = document.createElement(
+ customInputElements[components[i][j].field] || 'input');
+ input.setAttribute('field', components[i][j].field);
+ input.classList.add(components[i][j].length);
+ input.setAttribute('placeholder', components[i][j].placeholder || '');
+ fieldContainer.appendChild(input);
+
+ if (input.tagName == 'LIST') {
+ options.autofillOptions.AutofillValuesList.decorate(input);
+ input.autoExpands = true;
+ }
+ }
+ }
},
};
@@ -287,14 +396,19 @@ cr.define('options', function() {
AutofillEditAddressOverlay.getInstance().loadAddress_(address);
};
+ AutofillEditAddressOverlay.loadAddressComponents = function(input) {
+ AutofillEditAddressOverlay.getInstance().loadAddressComponents_(input);
+ };
+
AutofillEditAddressOverlay.setTitle = function(title) {
$('autofill-address-title').textContent = title;
};
AutofillEditAddressOverlay.setValidatedPhoneNumbers = function(numbers) {
- AutofillEditAddressOverlay.getInstance().setMultiValueList_('phone-list',
- numbers);
- $('phone-list').didReceiveValidationResult();
+ var instance = AutofillEditAddressOverlay.getInstance();
+ var phoneList = instance.pageDiv.querySelector('[field=phone]');
+ instance.setMultiValueList_(phoneList, numbers);
+ phoneList.didReceiveValidationResult();
};
// Export
diff --git a/chrome/browser/resources/options/autofill_edit_overlay.css b/chrome/browser/resources/options/autofill_edit_overlay.css
index 166f0cd..5a302fc 100644
--- a/chrome/browser/resources/options/autofill_edit_overlay.css
+++ b/chrome/browser/resources/options/autofill_edit_overlay.css
@@ -10,14 +10,21 @@
min-width: 500px;
}
-#full-name-list input,
-#company-name,
-#addr-line-1,
-#addr-line-2 {
+#autofill-edit-address-overlay .long div[role='listitem'] > div > div,
+#autofill-edit-address-overlay .long input,
+#autofill-edit-address-overlay textarea.long,
+#autofill-edit-address-overlay input.long {
width: 16em;
}
-#country {
+#autofill-edit-address-overlay .short div[role='listitem'] > div > div,
+#autofill-edit-address-overlay .short input,
+#autofill-edit-address-overlay textarea.short,
+#autofill-edit-address-overlay input.short {
+ width: 14em;
+}
+
+#autofill-edit-address-overlay .country {
max-width: 450px;
}
@@ -40,38 +47,10 @@
}
:-webkit-any(#autofill-edit-credit-card-overlay, #autofill-edit-address-overlay)
- .settings-row div + :-webkit-any(input, select) {
+ .settings-row div + :-webkit-any(input, select, textarea) {
margin-top: 4px;
}
-#autofill-name-labels {
- display: -webkit-inline-box;
-}
-
-#autofill-name-labels span {
- -webkit-box-flex: 1;
- display: block;
-}
-
-#full-name-list {
- display: inline-block;
-}
-
-#full-name-list div[role='listitem'] > div {
- display: -webkit-box;
-}
-
-#full-name-list div[role='listitem'] > div > div,
-#autofill-name-labels span {
- -webkit-margin-end: 5px;
- width: 16em;
-}
-
-:-webkit-any(#phone-list, #email-list) div[role='listitem'] > div > div,
-:-webkit-any(#phone-list, #email-list) input {
- width: 14em;
-}
-
.input-group > * {
-webkit-box-orient: vertical;
-webkit-margin-end: 2px;
diff --git a/chrome/browser/resources/options/autofill_options_list.js b/chrome/browser/resources/options/autofill_options_list.js
index 540bf56..8fd7fdb 100644
--- a/chrome/browser/resources/options/autofill_options_list.js
+++ b/chrome/browser/resources/options/autofill_options_list.js
@@ -489,7 +489,8 @@ cr.define('options.autofillOptions', function() {
var info = new Array();
info[0] = index;
info[1] = numbers;
- info[2] = $('country').value;
+ info[2] = document.querySelector(
+ '#autofill-edit-address-overlay [field=country]').value;
this.validationRequests_++;
chrome.send('validatePhoneNumbers', info);
},
diff --git a/chrome/browser/ui/webui/DEPS b/chrome/browser/ui/webui/DEPS
index 7d9e166..f3b4c54 100644
--- a/chrome/browser/ui/webui/DEPS
+++ b/chrome/browser/ui/webui/DEPS
@@ -11,6 +11,7 @@ include_rules = [
"+device/nfc",
"+third_party/angle", # For ANGLE version.
"+third_party/zlib/zlib.h", # For compression level constants.
+ "+third_party/libaddressinput/chromium/cpp/include", # For i18n address input.
# DOM Distiller.
"+components/dom_distiller/core",
diff --git a/chrome/browser/ui/webui/options/autofill_options_browsertest.js b/chrome/browser/ui/webui/options/autofill_options_browsertest.js
index 690f292..4cbd76d 100644
--- a/chrome/browser/ui/webui/options/autofill_options_browsertest.js
+++ b/chrome/browser/ui/webui/options/autofill_options_browsertest.js
@@ -3,6 +3,26 @@
// found in the LICENSE file.
/**
+ * Returns the HTML element for the |field|.
+ * @param {string} field The field name for the element.
+ * @return {HTMLElement} The HTML element.
+ */
+function getField(field) {
+ return document.querySelector(
+ '#autofill-edit-address-overlay [field=' + field + ']');
+}
+
+/**
+ * Returns the size of the |list|.
+ * @param {HTMLElement} list The list to check.
+ * @return {int} The size of the list.
+ */
+function getListSize(list) {
+ // Remove 1 for placeholder input field.
+ return list.items.length - 1;
+}
+
+/**
* TestFixture for autofill options WebUI testing.
* @extends {testing.Test}
* @constructor
@@ -47,7 +67,7 @@ TEST_F('AutofillEditAddressWebUITest',
function() {
assertEquals(this.browsePreload, document.location.href);
- var phoneList = $('phone-list');
+ var phoneList = getField('phone');
expectEquals(0, phoneList.validationRequests_);
phoneList.doneValidating().then(function() {
phoneList.focus();
@@ -61,3 +81,99 @@ TEST_F('AutofillEditAddressWebUITest',
});
});
});
+
+TEST_F('AutofillEditAddressWebUITest',
+ 'testInitialFormLayout',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ assertEquals(getField('country').value, '');
+ assertEquals(0, getListSize(getField('phone')));
+ assertEquals(0, getListSize(getField('email')));
+ assertEquals(0, getListSize(getField('fullName')));
+ assertEquals('', getField('city').value);
+
+ testDone();
+});
+
+TEST_F('AutofillEditAddressWebUITest',
+ 'testLoadAddress',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ var testAddress = {
+ guid: 'GUID Value',
+ fullName: ['Full Name 1', 'Full Name 2'],
+ companyName: 'Company Name Value',
+ addrLines: 'First Line Value\nSecond Line Value',
+ dependentLocality: 'Dependent Locality Value',
+ city: 'City Value',
+ state: 'State Value',
+ postalCode: 'Postal Code Value',
+ sortingCode: 'Sorting Code Value',
+ country: 'CH',
+ phone: ['123', '456'],
+ email: ['a@b.c', 'x@y.z'],
+ languageCode: 'de',
+ components: [[
+ {field: 'postalCode', length: 'short'},
+ {field: 'sortingCode', length: 'short'},
+ {field: 'dependentLocality', length: 'short'},
+ {field: 'city', length: 'short'},
+ {field: 'state', length: 'short'},
+ {field: 'addrLines', length: 'long'},
+ {field: 'companyName', length: 'long'},
+ {field: 'country', length: 'long'},
+ {field: 'fullName', length: 'long', placeholder: 'Add name'}
+ ]]
+ };
+ AutofillEditAddressOverlay.loadAddress(testAddress);
+
+ assertEquals(testAddress.guid, AutofillEditAddressOverlay.getInstance().guid);
+ assertEquals(testAddress.languageCode,
+ AutofillEditAddressOverlay.getInstance().languageCode);
+
+ var lists = ['fullName', 'email', 'phone'];
+ for (var i in lists) {
+ var field = getField(lists[i]);
+ assertEquals(testAddress[lists[i]].length, getListSize(field));
+ assertTrue(field.getAttribute('placeholder').length > 0);
+ assertTrue(field instanceof cr.ui.List);
+ }
+
+ var inputs = ['companyName', 'dependentLocality', 'city', 'state',
+ 'postalCode', 'sortingCode'];
+ for (var i in inputs) {
+ var field = getField(inputs[i]);
+ assertEquals(testAddress[inputs[i]], field.value);
+ assertTrue(field instanceof HTMLInputElement);
+ }
+
+ var addrLines = getField('addrLines');
+ assertEquals(testAddress.addrLines, addrLines.value);
+ assertTrue(addrLines instanceof HTMLTextAreaElement);
+
+ var country = getField('country');
+ assertEquals(testAddress.country, country.value);
+ assertTrue(country instanceof HTMLSelectElement);
+
+ testDone();
+});
+
+TEST_F('AutofillEditAddressWebUITest',
+ 'testLoadAddressComponents',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ var testInput = {
+ languageCode: 'fr',
+ components: [[{field: 'city'}],
+ [{field: 'state'}]]
+ };
+ AutofillEditAddressOverlay.loadAddressComponents(testInput);
+
+ assertEquals('fr', AutofillEditAddressOverlay.getInstance().languageCode);
+ expectEquals(2, $('autofill-edit-address-fields').children.length);
+
+ testDone();
+});
diff --git a/chrome/browser/ui/webui/options/autofill_options_handler.cc b/chrome/browser/ui/webui/options/autofill_options_handler.cc
index 60b5aeb..93b2351 100644
--- a/chrome/browser/ui/webui/options/autofill_options_handler.cc
+++ b/chrome/browser/ui/webui/options/autofill_options_handler.cc
@@ -28,6 +28,9 @@
#include "content/public/browser/web_ui.h"
#include "grit/component_strings.h"
#include "grit/generated_resources.h"
+#include "grit/libaddressinput_strings.h"
+#include "third_party/libaddressinput/chromium/cpp/include/libaddressinput/address_ui.h"
+#include "third_party/libaddressinput/chromium/cpp/include/libaddressinput/address_ui_component.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
@@ -36,11 +39,107 @@ using autofill::ServerFieldType;
using autofill::AutofillProfile;
using autofill::CreditCard;
using autofill::PersonalDataManager;
+using i18n::addressinput::AddressUiComponent;
namespace {
const char kSettingsOrigin[] = "Chrome settings";
+static const char kFullNameField[] = "fullName";
+static const char kCompanyNameField[] = "companyName";
+static const char kAddressLineField[] = "addrLines";
+static const char kDependentLocalityField[] = "dependentLocality";
+static const char kCityField[] = "city";
+static const char kStateField[] = "state";
+static const char kPostalCodeField[] = "postalCode";
+static const char kSortingCodeField[] = "sortingCode";
+static const char kCountryField[] = "country";
+
+static const char kComponents[] = "components";
+static const char kLanguageCode[] = "languageCode";
+
+// Fills |components| with the address UI components that should be used to
+// input an address for |country_code| when UI BCP 47 language code is
+// |ui_language_code|. If |components_language_code| is not NULL, then sets it
+// to the BCP 47 language code that should be used to format the address for
+// display.
+void GetAddressComponents(const std::string& country_code,
+ const std::string& ui_language_code,
+ base::ListValue* address_components,
+ std::string* components_language_code) {
+ DCHECK(address_components);
+
+ std::vector<AddressUiComponent> components =
+ i18n::addressinput::BuildComponents(
+ country_code, ui_language_code, components_language_code);
+ if (components.empty()) {
+ static const char kDefaultCountryCode[] = "US";
+ components = i18n::addressinput::BuildComponents(
+ kDefaultCountryCode, ui_language_code, components_language_code);
+ }
+ DCHECK(!components.empty());
+
+ base::ListValue* line = NULL;
+ static const char kField[] = "field";
+ static const char kLength[] = "length";
+ for (size_t i = 0; i < components.size(); ++i) {
+ if (i == 0 ||
+ components[i - 1].length_hint == AddressUiComponent::HINT_LONG ||
+ components[i].length_hint == AddressUiComponent::HINT_LONG) {
+ line = new base::ListValue;
+ address_components->Append(line);
+ }
+
+ scoped_ptr<base::DictionaryValue> component(new base::DictionaryValue);
+ component->SetString(
+ "name", l10n_util::GetStringUTF16(components[i].name_id));
+
+ switch (components[i].field) {
+ case i18n::addressinput::COUNTRY:
+ component->SetString(kField, kCountryField);
+ break;
+ case i18n::addressinput::ADMIN_AREA:
+ component->SetString(kField, kStateField);
+ break;
+ case i18n::addressinput::LOCALITY:
+ component->SetString(kField, kCityField);
+ break;
+ case i18n::addressinput::DEPENDENT_LOCALITY:
+ component->SetString(kField, kDependentLocalityField);
+ break;
+ case i18n::addressinput::SORTING_CODE:
+ component->SetString(kField, kSortingCodeField);
+ break;
+ case i18n::addressinput::POSTAL_CODE:
+ component->SetString(kField, kPostalCodeField);
+ break;
+ case i18n::addressinput::STREET_ADDRESS:
+ component->SetString(kField, kAddressLineField);
+ break;
+ case i18n::addressinput::ORGANIZATION:
+ component->SetString(kField, kCompanyNameField);
+ break;
+ case i18n::addressinput::RECIPIENT:
+ component->SetString(kField, kFullNameField);
+ component->SetString(
+ "placeholder",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADD_NAME));
+ break;
+ }
+
+ switch (components[i].length_hint) {
+ case AddressUiComponent::HINT_LONG:
+ component->SetString(kLength, "long");
+ break;
+ case AddressUiComponent::HINT_SHORT:
+ component->SetString(kLength, "short");
+ break;
+ }
+
+ line->Append(component.release());
+ }
+}
+
// Sets data related to the country <select>.
void SetCountryData(const PersonalDataManager& manager,
base::DictionaryValue* localized_strings) {
@@ -52,8 +151,6 @@ void SetCountryData(const PersonalDataManager& manager,
// An ordered list of options to show in the <select>.
scoped_ptr<base::ListValue> country_list(new base::ListValue());
- // A dictionary of postal code and state info, keyed on country code.
- scoped_ptr<base::DictionaryValue> country_data(new base::DictionaryValue());
for (size_t i = 0; i < countries.size(); ++i) {
scoped_ptr<base::DictionaryValue> option_details(
new base::DictionaryValue());
@@ -62,18 +159,19 @@ void SetCountryData(const PersonalDataManager& manager,
"value",
countries[i] ? countries[i]->country_code() : "separator");
country_list->Append(option_details.release());
-
- if (!countries[i])
- continue;
-
- scoped_ptr<base::DictionaryValue> details(new base::DictionaryValue());
- details->SetString("postalCodeLabel", countries[i]->postal_code_label());
- details->SetString("stateLabel", countries[i]->state_label());
- country_data->Set(countries[i]->country_code(), details.release());
-
}
localized_strings->Set("autofillCountrySelectList", country_list.release());
- localized_strings->Set("autofillCountryData", country_data.release());
+
+ scoped_ptr<base::ListValue> defaultCountryComponents(new base::ListValue);
+ std::string defaultCountryLanguageCode;
+ GetAddressComponents(countries.front()->country_code(),
+ g_browser_process->GetApplicationLocale(),
+ defaultCountryComponents.get(),
+ &defaultCountryLanguageCode);
+ localized_strings->Set("autofillDefaultCountryComponents",
+ defaultCountryComponents.release());
+ localized_strings->SetString("autofillDefaultCountryLanguageCode",
+ defaultCountryLanguageCode);
}
// Get the multi-valued element for |type| and return it in |ListValue| form.
@@ -107,68 +205,6 @@ void SetValueList(const base::ListValue* list,
profile->SetRawMultiInfo(type, values);
}
-// Get the multi-valued element for |type| and return it in |ListValue| form.
-void GetNameList(const AutofillProfile& profile,
- scoped_ptr<base::ListValue>* names) {
- names->reset(new base::ListValue);
-
- std::vector<base::string16> first_names;
- std::vector<base::string16> middle_names;
- std::vector<base::string16> last_names;
- profile.GetRawMultiInfo(autofill::NAME_FIRST, &first_names);
- profile.GetRawMultiInfo(autofill::NAME_MIDDLE, &middle_names);
- profile.GetRawMultiInfo(autofill::NAME_LAST, &last_names);
- DCHECK_EQ(first_names.size(), middle_names.size());
- DCHECK_EQ(first_names.size(), last_names.size());
-
- // |GetRawMultiInfo()| always returns at least one, potentially empty, item.
- if (first_names.size() == 1 && first_names.front().empty() &&
- middle_names.front().empty() && last_names.front().empty()) {
- return;
- }
-
- for (size_t i = 0; i < first_names.size(); ++i) {
- base::ListValue* name = new base::ListValue; // owned by |list|
- name->Set(0, new base::StringValue(first_names[i]));
- name->Set(1, new base::StringValue(middle_names[i]));
- name->Set(2, new base::StringValue(last_names[i]));
- (*names)->Set(i, name);
- }
-}
-
-// Set the multi-valued element for |type| from input |list| values.
-void SetNameList(const base::ListValue* names, AutofillProfile* profile) {
- const size_t size = names->GetSize();
- std::vector<base::string16> first_names(size);
- std::vector<base::string16> middle_names(size);
- std::vector<base::string16> last_names(size);
-
- for (size_t i = 0; i < size; ++i) {
- const base::ListValue* name;
- bool success = names->GetList(i, &name);
- DCHECK(success);
-
- base::string16 first_name;
- success = name->GetString(0, &first_name);
- DCHECK(success);
- first_names[i] = first_name;
-
- base::string16 middle_name;
- success = name->GetString(1, &middle_name);
- DCHECK(success);
- middle_names[i] = middle_name;
-
- base::string16 last_name;
- success = name->GetString(2, &last_name);
- DCHECK(success);
- last_names[i] = last_name;
- }
-
- profile->SetRawMultiInfo(autofill::NAME_FIRST, first_names);
- profile->SetRawMultiInfo(autofill::NAME_MIDDLE, middle_names);
- profile->SetRawMultiInfo(autofill::NAME_LAST, last_names);
-}
-
// Pulls the phone number |index|, |phone_number_list|, and |country_code| from
// the |args| input.
void ExtractPhoneNumberInformation(const base::ListValue* args,
@@ -306,6 +342,10 @@ void AutofillOptionsHandler::RegisterMessages() {
base::Bind(&AutofillOptionsHandler::LoadAddressEditor,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ "loadAddressEditorComponents",
+ base::Bind(&AutofillOptionsHandler::LoadAddressEditorComponents,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
"loadCreditCardEditor",
base::Bind(&AutofillOptionsHandler::LoadCreditCardEditor,
base::Unretained(this)));
@@ -332,32 +372,12 @@ void AutofillOptionsHandler::SetAddressOverlayStrings(
base::DictionaryValue* localized_strings) {
localized_strings->SetString("autofillEditAddressTitle",
l10n_util::GetStringUTF16(IDS_AUTOFILL_EDIT_ADDRESS_CAPTION));
- localized_strings->SetString("autofillFirstNameLabel",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_FIRST_NAME));
- localized_strings->SetString("autofillMiddleNameLabel",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_MIDDLE_NAME));
- localized_strings->SetString("autofillLastNameLabel",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_LAST_NAME));
- localized_strings->SetString("autofillCompanyNameLabel",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_COMPANY_NAME));
- localized_strings->SetString("autofillAddrLine1Label",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADDRESS_LINE_1));
- localized_strings->SetString("autofillAddrLine2Label",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADDRESS_LINE_2));
- localized_strings->SetString("autofillCityLabel",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_CITY));
localized_strings->SetString("autofillCountryLabel",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_COUNTRY));
+ l10n_util::GetStringUTF16(IDS_LIBADDRESSINPUT_I18N_COUNTRY_LABEL));
localized_strings->SetString("autofillPhoneLabel",
l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_PHONE));
localized_strings->SetString("autofillEmailLabel",
l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_EMAIL));
- localized_strings->SetString("autofillAddFirstNamePlaceholder",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADD_FIRST_NAME));
- localized_strings->SetString("autofillAddMiddleNamePlaceholder",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADD_MIDDLE_NAME));
- localized_strings->SetString("autofillAddLastNamePlaceholder",
- l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADD_LAST_NAME));
localized_strings->SetString("autofillAddPhonePlaceholder",
l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADD_PHONE));
localized_strings->SetString("autofillAddEmailPlaceholder",
@@ -451,27 +471,60 @@ void AutofillOptionsHandler::LoadAddressEditor(const base::ListValue* args) {
base::DictionaryValue address;
address.SetString("guid", profile->guid());
scoped_ptr<base::ListValue> list;
- GetNameList(*profile, &list);
- address.Set("fullName", list.release());
- address.SetString("companyName", profile->GetRawInfo(autofill::COMPANY_NAME));
- address.SetString("addrLine1",
- profile->GetRawInfo(autofill::ADDRESS_HOME_LINE1));
- address.SetString("addrLine2",
- profile->GetRawInfo(autofill::ADDRESS_HOME_LINE2));
- address.SetString("city", profile->GetRawInfo(autofill::ADDRESS_HOME_CITY));
- address.SetString("state", profile->GetRawInfo(autofill::ADDRESS_HOME_STATE));
- address.SetString("postalCode",
+ GetValueList(*profile, autofill::NAME_FULL, &list);
+ address.Set(kFullNameField, list.release());
+ address.SetString(
+ kCompanyNameField, profile->GetRawInfo(autofill::COMPANY_NAME));
+ address.SetString(kAddressLineField,
+ profile->GetRawInfo(autofill::ADDRESS_HOME_STREET_ADDRESS));
+ address.SetString(
+ kCityField, profile->GetRawInfo(autofill::ADDRESS_HOME_CITY));
+ address.SetString(
+ kStateField, profile->GetRawInfo(autofill::ADDRESS_HOME_STATE));
+ address.SetString(
+ kDependentLocalityField,
+ profile->GetRawInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY));
+ address.SetString(kSortingCodeField,
+ profile->GetRawInfo(autofill::ADDRESS_HOME_SORTING_CODE));
+ address.SetString(kPostalCodeField,
profile->GetRawInfo(autofill::ADDRESS_HOME_ZIP));
- address.SetString("country",
+ address.SetString(kCountryField,
profile->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
GetValueList(*profile, autofill::PHONE_HOME_WHOLE_NUMBER, &list);
address.Set("phone", list.release());
GetValueList(*profile, autofill::EMAIL_ADDRESS, &list);
address.Set("email", list.release());
+ address.SetString(kLanguageCode, profile->language_code());
+
+ scoped_ptr<base::ListValue> components(new base::ListValue);
+ GetAddressComponents(
+ base::UTF16ToUTF8(profile->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY)),
+ profile->language_code(), components.get(), NULL);
+ address.Set(kComponents, components.release());
web_ui()->CallJavascriptFunction("AutofillOptions.editAddress", address);
}
+void AutofillOptionsHandler::LoadAddressEditorComponents(
+ const base::ListValue* args) {
+ std::string country_code;
+ if (!args->GetString(0, &country_code)) {
+ NOTREACHED();
+ return;
+ }
+
+ base::DictionaryValue input;
+ scoped_ptr<base::ListValue> components(new base::ListValue);
+ std::string language_code;
+ GetAddressComponents(country_code, g_browser_process->GetApplicationLocale(),
+ components.get(), &language_code);
+ input.Set(kComponents, components.release());
+ input.SetString(kLanguageCode, language_code);
+
+ web_ui()->CallJavascriptFunction(
+ "AutofillEditAddressOverlay.loadAddressComponents", input);
+}
+
void AutofillOptionsHandler::LoadCreditCardEditor(const base::ListValue* args) {
DCHECK(IsPersonalDataLoaded());
@@ -514,48 +567,53 @@ void AutofillOptionsHandler::SetAddress(const base::ListValue* args) {
if (!IsPersonalDataLoaded())
return;
+ int arg_counter = 0;
std::string guid;
- if (!args->GetString(0, &guid)) {
+ if (!args->GetString(arg_counter++, &guid)) {
NOTREACHED();
return;
}
AutofillProfile profile(guid, kSettingsOrigin);
- std::string country_code;
base::string16 value;
const base::ListValue* list_value;
- if (args->GetList(1, &list_value))
- SetNameList(list_value, &profile);
+ if (args->GetList(arg_counter++, &list_value))
+ SetValueList(list_value, autofill::NAME_FULL, &profile);
- if (args->GetString(2, &value))
+ if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::COMPANY_NAME, value);
- if (args->GetString(3, &value))
- profile.SetRawInfo(autofill::ADDRESS_HOME_LINE1, value);
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_STREET_ADDRESS, value);
- if (args->GetString(4, &value))
- profile.SetRawInfo(autofill::ADDRESS_HOME_LINE2, value);
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY, value);
- if (args->GetString(5, &value))
+ if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::ADDRESS_HOME_CITY, value);
- if (args->GetString(6, &value))
+ if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::ADDRESS_HOME_STATE, value);
- if (args->GetString(7, &value))
+ if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::ADDRESS_HOME_ZIP, value);
- if (args->GetString(8, &country_code))
- profile.SetRawInfo(autofill::ADDRESS_HOME_COUNTRY,
- base::ASCIIToUTF16(country_code));
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_SORTING_CODE, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_COUNTRY, value);
- if (args->GetList(9, &list_value))
+ if (args->GetList(arg_counter++, &list_value))
SetValueList(list_value, autofill::PHONE_HOME_WHOLE_NUMBER, &profile);
- if (args->GetList(10, &list_value))
+ if (args->GetList(arg_counter++, &list_value))
SetValueList(list_value, autofill::EMAIL_ADDRESS, &profile);
+ if (args->GetString(arg_counter++, &value))
+ profile.set_language_code(base::UTF16ToUTF8(value));
+
if (!base::IsValidGUID(profile.guid())) {
profile.set_guid(base::GenerateGUID());
personal_data_->AddProfile(profile);
diff --git a/chrome/browser/ui/webui/options/autofill_options_handler.h b/chrome/browser/ui/webui/options/autofill_options_handler.h
index 5d58d21..a98a986 100644
--- a/chrome/browser/ui/webui/options/autofill_options_handler.h
+++ b/chrome/browser/ui/webui/options/autofill_options_handler.h
@@ -55,6 +55,11 @@ class AutofillOptionsHandler : public OptionsPageUIHandler,
// |args| - A string, the GUID of the address to load.
void LoadAddressEditor(const base::ListValue* args);
+ // Requests input form layout information for a specific country code. Calls
+ // into WebUI with the layout information.
+ // |args| - A string, the country code to load.
+ void LoadAddressEditorComponents(const base::ListValue* args);
+
// Requests profile data for a specific credit card. Calls into WebUI with the
// loaded profile data to open the credit card editor.
// |args| - A string, the GUID of the credit card to load.