diff --git a/app/extensions/brave/about-autofill.html b/app/extensions/brave/about-autofill.html
new file mode 100644
index 00000000000..40ec9641bcf
--- /dev/null
+++ b/app/extensions/brave/about-autofill.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/extensions/brave/locales/en-US/app.properties b/app/extensions/brave/locales/en-US/app.properties
index 45e86970e4b..ecaa2446a37 100644
--- a/app/extensions/brave/locales/en-US/app.properties
+++ b/app/extensions/brave/locales/en-US/app.properties
@@ -177,3 +177,16 @@ allSiteCookies=All site cookies
clear=Clear
clearDataWarning=Warning: Selected data, back to the day you installed Brave will be cleared and cannot be undone.
clearBrowsingData=Clear browsing data
+name=Name
+creditCardNumber=Card Number
+expirationDate=Expiration date
+organization=Organization
+streetAddress=Street Address
+city=City
+state=State
+postalCode=Postal Code
+country=Country
+phone=Phone
+email=Email
+editAddress=Edit Address
+editCreditCard=Edit Credit Card
diff --git a/app/extensions/brave/locales/en-US/autofill.properties b/app/extensions/brave/locales/en-US/autofill.properties
new file mode 100644
index 00000000000..79f5bb6ac36
--- /dev/null
+++ b/app/extensions/brave/locales/en-US/autofill.properties
@@ -0,0 +1,18 @@
+autofillTitle=Autofill Settings
+addresses=Addresses
+creditCards=Credit Cards
+addAddress=Add Address
+addCreditCard=Add Credit Card
+name=Name
+creditCardNumber=Card Number
+expirationDate=Expiration date
+noCreditCardsSaved=No saved credit cards
+organization=Organization
+streetAddress=Street Address
+city=City
+state=State
+postalCode=Postal Code
+country=Country
+phone=Phone
+email=Email
+noAddressesSaved=No saved addresses
diff --git a/app/extensions/brave/locales/en-US/preferences.properties b/app/extensions/brave/locales/en-US/preferences.properties
index 281f1bce6c6..d58d2bd9b46 100644
--- a/app/extensions/brave/locales/en-US/preferences.properties
+++ b/app/extensions/brave/locales/en-US/preferences.properties
@@ -172,3 +172,6 @@ allSiteCookies=All site cookies
passwordsAndForms=Passwords and Forms
tabSettings=Tab Settings
clearBrowsingDataNow=Clear Browsing Data Now...
+autofillSettings=Autofill Settings
+manageAutofillData=Manage Autofill Data...
+enableAutofill=Enable Autofill
diff --git a/app/filtering.js b/app/filtering.js
index aa1b38b5e8d..a7804619a14 100644
--- a/app/filtering.js
+++ b/app/filtering.js
@@ -579,3 +579,56 @@ module.exports.setDefaultZoomLevel = (zoom) => {
ses.userPrefs.setDefaultZoomLevel(zoom)
}
}
+
+module.exports.addAutofillAddress = (detail) => {
+ let guidMap = {}
+ for (let partition in registeredSessions) {
+ let ses = registeredSessions[partition]
+ let guid = ses.autofill.addProfile({
+ full_name: detail.name,
+ company_name: detail.organization,
+ street_address: detail.streetAddress,
+ city: detail.city,
+ state: detail.state,
+ postal_code: detail.postalCode,
+ country_code: detail.country,
+ phone: detail.phone,
+ email: detail.email
+ })
+ guidMap[partition] = guid
+ }
+ return guidMap
+}
+
+module.exports.removeAutofillAddress = (guid) => {
+ for (let partition in registeredSessions) {
+ let ses = registeredSessions[partition]
+ if (guid[partition] !== undefined) {
+ ses.autofill.removeProfile(guid[partition])
+ }
+ }
+}
+
+module.exports.addAutofillCreditCard = (detail) => {
+ let guidMap = {}
+ for (let partition in registeredSessions) {
+ let ses = registeredSessions[partition]
+ let guid = ses.autofill.addCreditCard({
+ name: detail.name,
+ card_number: detail.card,
+ expiration_month: detail.month,
+ expiration_year: detail.year
+ })
+ guidMap[partition] = guid
+ }
+ return guidMap
+}
+
+module.exports.removeAutofillCreditCard = (guid) => {
+ for (let partition in registeredSessions) {
+ let ses = registeredSessions[partition]
+ if (guid[partition] !== undefined) {
+ ses.autofill.removeCreditCard(guid[partition])
+ }
+ }
+}
diff --git a/app/index.js b/app/index.js
index 1cdf424618d..c895214f734 100644
--- a/app/index.js
+++ b/app/index.js
@@ -61,6 +61,7 @@ const spellCheck = require('./spellCheck')
const ledger = require('./ledger')
const flash = require('../js/flash')
const contentSettings = require('../js/state/contentSettings')
+const privacy = require('../js/state/privacy')
// Used to collect the per window state when shutting down the application
let perWindowState = []
@@ -392,6 +393,7 @@ app.on('ready', () => {
return loadedPerWindowState
}).then((loadedPerWindowState) => {
contentSettings.init()
+ privacy.init()
Extensions.init()
Filtering.init()
SiteHacks.init()
@@ -747,6 +749,14 @@ app.on('ready', () => {
}
})
+ ipcMain.on(messages.REMOVE_AUTOFILL_ADDRESS, (e, address) => {
+ appActions.removeAutofillAddress(address)
+ })
+
+ ipcMain.on(messages.REMOVE_AUTOFILL_CREDIT_CARD, (e, card) => {
+ appActions.removeAutofillCreditCard(card)
+ })
+
// Setup the crash handling
CrashHerald.init()
diff --git a/docs/appActions.md b/docs/appActions.md
index 4948ca496c3..7dd1dc7af7e 100644
--- a/docs/appActions.md
+++ b/docs/appActions.md
@@ -332,6 +332,50 @@ Sets whether the bitcoin protocol is handled.
+### addAutofillAddress(detail, originalDetail)
+
+Add address data
+
+**Parameters**
+
+**detail**: `object`, the address to add as per doc/state.md's autofillAddressDetail
+
+**originalDetail**: `object`, the original address before editing
+
+
+
+### removeAutofillAddress(detail)
+
+Remove address data
+
+**Parameters**
+
+**detail**: `object`, the address to remove as per doc/state.md's autofillAddressDetail
+
+
+
+### addAutofillCreditCard(detail, originalDetail)
+
+Add credit card data
+
+**Parameters**
+
+**detail**: `object`, the credit card to add as per doc/state.md's autofillCreditCardDetail
+
+**originalDetail**: `object`, the original credit card before editing
+
+
+
+### removeAutofillCreditCard(detail)
+
+Remove credit card data
+
+**Parameters**
+
+**detail**: `object`, the credit card to remove as per doc/state.md's autofillCreditCardDetail
+
+
+
* * *
diff --git a/docs/state.md b/docs/state.md
index 4b9c79b1a39..f4947f433c0 100644
--- a/docs/state.md
+++ b/docs/state.md
@@ -169,6 +169,14 @@ AppStore
locale: string, // en_US, en, or any other locale string
ignoredWords: Array, // List of words to ignore
addedWords: Array // List of words to add to the dictionary
+ },
+ autofill: {
+ addresses: [{
+ Object.> // map of (partition, id) used to access the autofill entry in database
+ }],
+ creditCards: [{
+ Object.> // map of (partition, id) used to access the autofill entry in database
+ }]
}
}
```
@@ -411,6 +419,25 @@ WindowStore
score: ?
}
},
- hasBitcoinHandler: boolean // Whether Brave has a bitcoin: protocol handler
+ hasBitcoinHandler: boolean, // Whether Brave has a bitcoin: protocol handler
+ autofillAddressDetail: {
+ name: string,
+ organization: string,
+ streetAddress: string,
+ city: string,
+ state: string,
+ postalCode: string,
+ country: string,
+ phone: string,
+ email: string,
+ guid: Object.> // map of (partition, id) used to access the autofill entry in database
+ },
+ autofillCreditCardDetail: {
+ name: string,
+ card: string,
+ month: string,
+ year: string,
+ guid: Object.> // map of (partition, id) used to access the autofill entry in database
+ }
}
```
diff --git a/docs/windowActions.md b/docs/windowActions.md
index c8d47997fd0..a5057d0d68a 100644
--- a/docs/windowActions.md
+++ b/docs/windowActions.md
@@ -741,6 +741,30 @@ Sets the clear browsing data popup detail
+### setAutofillAddressDetail(currentDetail, originalDetail)
+
+Sets the manage autofill address popup detail
+
+**Parameters**
+
+**currentDetail**: `Object`, Properties of the address to change to
+
+**originalDetail**: `Object`, Properties of the address to edit
+
+
+
+### setAutofillCreditCardDetail(currentDetail, originalDetail)
+
+Sets the manage autofill credit card popup detail
+
+**Parameters**
+
+**currentDetail**: `Object`, Properties of the credit card to change to
+
+**originalDetail**: `Object`, Properties of the credit card to edit
+
+
+
* * *
diff --git a/js/about/aboutActions.js b/js/about/aboutActions.js
index 71eb1886276..09e579e7486 100644
--- a/js/about/aboutActions.js
+++ b/js/about/aboutActions.js
@@ -129,6 +129,60 @@ const AboutActions = {
createWallet: function () {
ipc.send(messages.LEDGER_CREATE_WALLET)
+ },
+
+ setLedgerEnabled: function (enabled) {
+ ipc.send(messages.LEDGER_ENABLE, enabled)
+ },
+
+ /**
+ * Open a adding address dialog
+ */
+ addAutofillAddress: function () {
+ ipc.sendToHost(messages.ADD_AUTOFILL_ADDRESS)
+ },
+
+ /**
+ * Remove address
+ *
+ * @param {object} address - address to remove as per doc/state.md's autofillAddressDetail
+ */
+ removeAutofillAddress: function (address) {
+ ipc.send(messages.REMOVE_AUTOFILL_ADDRESS, address)
+ },
+
+ /**
+ * Open a edit address dialog
+ *
+ * @param {object} address - address to edit as per doc/state.md's autofillAddressDetail
+ */
+ editAutofillAddress: function (address) {
+ ipc.sendToHost(messages.EDIT_AUTOFILL_ADDRESS, address)
+ },
+
+ /**
+ * Open a adding credit card dialog
+ */
+ addAutofillCreditCard: function () {
+ ipc.sendToHost(messages.ADD_AUTOFILL_CREDIT_CARD)
+ },
+
+ /**
+ * Remove credit card
+ *
+ * @param {object} card - credit card to remove as per doc/state.md's autofillCreditCardDetail
+ */
+ removeAutofillCreditCard: function (card) {
+ ipc.send(messages.REMOVE_AUTOFILL_CREDIT_CARD, card)
+ },
+
+ /**
+ * Open a editing credit card dialog
+ *
+ * @param {object} card - credit card to edit as per doc/state.md's autofillCreditCardDetail
+ */
+ editAutofillCreditCard: function (card) {
+ ipc.sendToHost(messages.EDIT_AUTOFILL_CREDIT_CARD, card)
}
}
module.exports = AboutActions
diff --git a/js/about/autofill.js b/js/about/autofill.js
new file mode 100644
index 00000000000..e1132de9a8c
--- /dev/null
+++ b/js/about/autofill.js
@@ -0,0 +1,210 @@
+/* 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/. */
+
+const React = require('react')
+const messages = require('../constants/messages')
+const Immutable = require('immutable')
+const ImmutableComponent = require('../components/immutableComponent')
+const aboutActions = require('./aboutActions')
+const Button = require('../components/button')
+
+const ipc = window.chrome.ipc
+
+require('../../less/about/autofill.less')
+require('../../node_modules/font-awesome/css/font-awesome.css')
+
+class AddressItem extends ImmutableComponent {
+ constructor () {
+ super()
+ this.onDelete = this.onDelete.bind(this)
+ this.onEdit = this.onEdit.bind(this)
+ }
+
+ onDelete () {
+ aboutActions.removeAutofillAddress(this.props.address.toJS())
+ }
+
+ onEdit () {
+ aboutActions.editAutofillAddress(this.props.address.toJS())
+ }
+
+ render () {
+ const address = this.props.address
+ return
+
+
+
+ |
+ {address.get('name')} |
+ {address.get('organization')} |
+ {address.get('streetAddress')} |
+ {address.get('city')} |
+ {address.get('state')} |
+ {address.get('postalCode')} |
+ {address.get('country')} |
+ {address.get('phone')} |
+ {address.get('email')} |
+
+
+
+ |
+
+ }
+}
+
+class CreditCardItem extends ImmutableComponent {
+ constructor () {
+ super()
+ this.onDelete = this.onDelete.bind(this)
+ this.onEdit = this.onEdit.bind(this)
+ }
+
+ onDelete () {
+ aboutActions.removeAutofillCreditCard(this.props.creditCard.toJS())
+ }
+
+ onEdit () {
+ aboutActions.editAutofillCreditCard(this.props.creditCard.toJS())
+ }
+
+ render () {
+ const creditCard = this.props.creditCard
+ return
+
+
+
+ |
+ {creditCard.get('name')} |
+ {
+ creditCard.get('card') !== undefined ? '***' + creditCard.get('card').slice(-4) : null
+ } |
+
+ {creditCard.get('month') + '/' + creditCard.get('year')}
+ |
+
+
+
+ |
+
+ }
+}
+
+class AboutAutofill extends React.Component {
+ constructor () {
+ super()
+ this.state = {
+ addressesDetails: new Immutable.List(),
+ creditCardsDetails: new Immutable.List()
+ }
+ ipc.on(messages.AUTOFILL_ADDRESSES_UPDATED, (e, detail) => {
+ if (detail) {
+ this.setState({
+ addressesDetails: Immutable.fromJS(detail)
+ })
+ }
+ })
+ ipc.on(messages.AUTOFILL_CREDIT_CARDS_UPDATED, (e, detail) => {
+ if (detail) {
+ this.setState({
+ creditCardsDetails: Immutable.fromJS(detail)
+ })
+ }
+ })
+ this.onAddAddress = this.onAddAddress.bind(this)
+ this.onAddCreditCard = this.onAddCreditCard.bind(this)
+ }
+
+ onAddAddress () {
+ aboutActions.addAutofillAddress()
+ }
+
+ onAddCreditCard () {
+ aboutActions.addAutofillCreditCard()
+ }
+
+ get isAddresssEmpty () {
+ return !this.state.addressesDetails || !this.state.addressesDetails.size
+ }
+
+ get isCreditCardsEmpty () {
+ return !this.state.creditCardsDetails || !this.state.creditCardsDetails.size
+ }
+
+ render () {
+ var savedAddresssPage = this.isAddresssEmpty
+ ?
+ :
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ {
+ this.state.addressesDetails.sort((a, b) => {
+ return a.get('name') > b.get('name') ? 1 : -1
+ }).map((item) =>
+ )
+ }
+
+
+
+
+ var savedCreditCardsPage = this.isCreditCardsEmpty
+ ?
+ :
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+ {
+ this.state.creditCardsDetails.sort((a, b) => {
+ return a.get('name') > b.get('name') ? 1 : -1
+ }).map((item) =>
+ )
+ }
+
+
+
+ return
+
+
+
+
+
+ {savedAddresssPage}
+
+
+
+
+
+ {savedCreditCardsPage}
+
+
+
+
+ }
+}
+
+module.exports =
diff --git a/js/about/entry.js b/js/about/entry.js
index 8891698c559..20351284766 100644
--- a/js/about/entry.js
+++ b/js/about/entry.js
@@ -38,6 +38,9 @@ switch (getBaseUrl(getSourceAboutUrl(window.location.href))) {
break
case 'about:history':
element = require('./history')
+ break
+ case 'about:autofill':
+ element = require('./autofill')
}
if (element) {
diff --git a/js/about/preferences.js b/js/about/preferences.js
index c95fb8be1bc..3a692603584 100644
--- a/js/about/preferences.js
+++ b/js/about/preferences.js
@@ -835,6 +835,14 @@ class SecurityTab extends ImmutableComponent {
: null
}
+
+
+
+
+
diff --git a/js/actions/appActions.js b/js/actions/appActions.js
index 04ef6339a19..f1348eae4d6 100644
--- a/js/actions/appActions.js
+++ b/js/actions/appActions.js
@@ -378,6 +378,54 @@ const appActions = {
actionType: AppConstants.APP_SET_BITCOIN_HANDLED,
handled
})
+ },
+
+ /**
+ * Add address data
+ * @param {object} detail - the address to add as per doc/state.md's autofillAddressDetail
+ * @param {object} originalDetail - the original address before editing
+ */
+ addAutofillAddress: function (detail, originalDetail) {
+ AppDispatcher.dispatch({
+ actionType: AppConstants.APP_ADD_AUTOFILL_ADDRESS,
+ detail,
+ originalDetail
+ })
+ },
+
+ /**
+ * Remove address data
+ * @param {object} detail - the address to remove as per doc/state.md's autofillAddressDetail
+ */
+ removeAutofillAddress: function (detail) {
+ AppDispatcher.dispatch({
+ actionType: AppConstants.APP_REMOVE_AUTOFILL_ADDRESS,
+ detail
+ })
+ },
+
+ /**
+ * Add credit card data
+ * @param {object} detail - the credit card to add as per doc/state.md's autofillCreditCardDetail
+ * @param {object} originalDetail - the original credit card before editing
+ */
+ addAutofillCreditCard: function (detail, originalDetail) {
+ AppDispatcher.dispatch({
+ actionType: AppConstants.APP_ADD_AUTOFILL_CREDIT_CARD,
+ detail,
+ originalDetail
+ })
+ },
+
+ /**
+ * Remove credit card data
+ * @param {object} detail - the credit card to remove as per doc/state.md's autofillCreditCardDetail
+ */
+ removeAutofillCreditCard: function (detail) {
+ AppDispatcher.dispatch({
+ actionType: AppConstants.APP_REMOVE_AUTOFILL_CREDIT_CARD,
+ detail
+ })
}
}
diff --git a/js/actions/windowActions.js b/js/actions/windowActions.js
index cb0ae8b455c..e9fd78dd8e8 100644
--- a/js/actions/windowActions.js
+++ b/js/actions/windowActions.js
@@ -960,6 +960,32 @@ const windowActions = {
actionType: WindowConstants.WINDOW_SET_CLEAR_BROWSING_DATA_DETAIL,
clearBrowsingDataDetail
})
+ },
+
+ /**
+ * Sets the manage autofill address popup detail
+ * @param {Object} currentDetail - Properties of the address to change to
+ * @param {Object} originalDetail - Properties of the address to edit
+ */
+ setAutofillAddressDetail: function (currentDetail, originalDetail) {
+ dispatch({
+ actionType: WindowConstants.WINDOW_SET_AUTOFILL_ADDRESS_DETAIL,
+ currentDetail,
+ originalDetail
+ })
+ },
+
+ /**
+ * Sets the manage autofill credit card popup detail
+ * @param {Object} currentDetail - Properties of the credit card to change to
+ * @param {Object} originalDetail - Properties of the credit card to edit
+ */
+ setAutofillCreditCardDetail: function (currentDetail, originalDetail) {
+ dispatch({
+ actionType: WindowConstants.WINDOW_SET_AUTOFILL_CREDIT_CARD_DETAIL,
+ currentDetail,
+ originalDetail
+ })
}
}
diff --git a/js/components/autofillAddressPanel.js b/js/components/autofillAddressPanel.js
new file mode 100644
index 00000000000..a6caa072e85
--- /dev/null
+++ b/js/components/autofillAddressPanel.js
@@ -0,0 +1,152 @@
+/* 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/. */
+
+const React = require('react')
+const ImmutableComponent = require('./immutableComponent')
+const Dialog = require('./dialog')
+const Button = require('./button')
+const windowActions = require('../actions/windowActions')
+const appActions = require('../actions/appActions')
+const KeyCodes = require('../constants/keyCodes')
+
+class AutofillAddressPanel extends ImmutableComponent {
+ constructor () {
+ super()
+ this.onNameChange = this.onNameChange.bind(this)
+ this.onOrganizationChange = this.onOrganizationChange.bind(this)
+ this.onStreetAddressChange = this.onStreetAddressChange.bind(this)
+ this.onCityChange = this.onCityChange.bind(this)
+ this.onStateChange = this.onStateChange.bind(this)
+ this.onPostalCodeChange = this.onPostalCodeChange.bind(this)
+ this.onCountryChange = this.onCountryChange.bind(this)
+ this.onPhoneChange = this.onPhoneChange.bind(this)
+ this.onEmailChange = this.onEmailChange.bind(this)
+ this.onKeyDown = this.onKeyDown.bind(this)
+ this.onSave = this.onSave.bind(this)
+ this.onClick = this.onClick.bind(this)
+ }
+ onNameChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('name', e.target.value)
+ windowActions.setAutofillAddressDetail(currentDetail, this.props.originalDetail)
+ }
+ onOrganizationChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('organization', e.target.value)
+ windowActions.setAutofillAddressDetail(currentDetail, this.props.originalDetail)
+ }
+ onStreetAddressChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('streetAddress', e.target.value)
+ windowActions.setAutofillAddressDetail(currentDetail, this.props.originalDetail)
+ }
+ onCityChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('city', e.target.value)
+ windowActions.setAutofillAddressDetail(currentDetail, this.props.originalDetail)
+ }
+ onStateChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('state', e.target.value)
+ windowActions.setAutofillAddressDetail(currentDetail, this.props.originalDetail)
+ }
+ onPostalCodeChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('postalCode', e.target.value)
+ windowActions.setAutofillAddressDetail(currentDetail, this.props.originalDetail)
+ }
+ onCountryChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('country', e.target.value)
+ windowActions.setAutofillAddressDetail(currentDetail, this.props.originalDetail)
+ }
+ onPhoneChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('phone', e.target.value)
+ windowActions.setAutofillAddressDetail(currentDetail, this.props.originalDetail)
+ }
+ onEmailChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('email', e.target.value)
+ windowActions.setAutofillAddressDetail(currentDetail, this.props.originalDetail)
+ }
+ onKeyDown (e) {
+ switch (e.keyCode) {
+ case KeyCodes.ENTER:
+ this.onSave()
+ break
+ case KeyCodes.ESC:
+ this.props.onHide()
+ break
+ }
+ }
+ onSave () {
+ appActions.addAutofillAddress(this.props.currentDetail, this.props.originalDetail)
+ this.props.onHide()
+ }
+ onClick (e) {
+ e.stopPropagation()
+ }
+ render () {
+ return
+ }
+}
+
+module.exports = AutofillAddressPanel
diff --git a/js/components/autofillCreditCardPanel.js b/js/components/autofillCreditCardPanel.js
new file mode 100644
index 00000000000..1fe38673b74
--- /dev/null
+++ b/js/components/autofillCreditCardPanel.js
@@ -0,0 +1,111 @@
+/* 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/. */
+
+const React = require('react')
+const ImmutableComponent = require('./immutableComponent')
+const Dialog = require('./dialog')
+const Button = require('./button')
+const windowActions = require('../actions/windowActions')
+const appActions = require('../actions/appActions')
+const KeyCodes = require('../constants/keyCodes')
+
+class AutofillCreditCardPanel extends ImmutableComponent {
+ constructor () {
+ super()
+ this.onNameChange = this.onNameChange.bind(this)
+ this.onCardChange = this.onCardChange.bind(this)
+ this.onExpMonthChange = this.onExpMonthChange.bind(this)
+ this.onExpYearChange = this.onExpYearChange.bind(this)
+ this.onKeyDown = this.onKeyDown.bind(this)
+ this.onSave = this.onSave.bind(this)
+ this.onClick = this.onClick.bind(this)
+ }
+ onNameChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('name', e.target.value)
+ windowActions.setAutofillCreditCardDetail(currentDetail, this.props.originalDetail)
+ }
+ onCardChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('card', e.target.value)
+ windowActions.setAutofillCreditCardDetail(currentDetail, this.props.originalDetail)
+ }
+ onExpMonthChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('month', e.target.value)
+ windowActions.setAutofillCreditCardDetail(currentDetail, this.props.originalDetail)
+ }
+ onExpYearChange (e) {
+ let currentDetail = this.props.currentDetail
+ currentDetail = currentDetail.set('year', e.target.value)
+ windowActions.setAutofillCreditCardDetail(currentDetail, this.props.originalDetail)
+ }
+ onKeyDown (e) {
+ switch (e.keyCode) {
+ case KeyCodes.ENTER:
+ this.onSave()
+ break
+ case KeyCodes.ESC:
+ this.props.onHide()
+ break
+ }
+ }
+ onSave () {
+ appActions.addAutofillCreditCard(this.props.currentDetail, this.props.originalDetail)
+ this.props.onHide()
+ }
+ onClick (e) {
+ e.stopPropagation()
+ }
+ get displayMonth () {
+ return this.props.currentDetail.get('month').replace('0', '')
+ }
+ render () {
+ var ExpMonth = []
+ for (let i = 1; i <= 12; ++i) {
+ ExpMonth.push()
+ }
+ var ExpYear = []
+ var today = new Date()
+ var year = today.getFullYear()
+ for (let i = year; i <= year + 9; ++i) {
+ ExpYear.push()
+ }
+
+ return
+ }
+}
+
+module.exports = AutofillCreditCardPanel
diff --git a/js/components/frame.js b/js/components/frame.js
index ff5c840759b..6c4b47875dd 100644
--- a/js/components/frame.js
+++ b/js/components/frame.js
@@ -108,6 +108,45 @@ class Frame extends ImmutableComponent {
}
} else if (location === 'about:flash') {
this.webview.send(messages.BRAVERY_DEFAULTS_UPDATED, this.braveryDefaults)
+ } else if (location === 'about:autofill') {
+ const partition = FrameStateUtil.getPartition(this.frame)
+ if (this.props.autofillAddresses) {
+ const addresses = this.props.autofillAddresses.toJS()
+ let list = []
+ for (let index in addresses) {
+ const address = currentWindow.webContents.session.autofill.getProfile(addresses[index][partition])
+ let addressDetail = {
+ name: address.full_name,
+ organization: address.company_name,
+ streetAddress: address.street_address,
+ city: address.city,
+ state: address.state,
+ postalCode: address.postal_code,
+ country: address.country_code,
+ phone: address.phone,
+ email: address.email,
+ guid: addresses[index]
+ }
+ list.push(addressDetail)
+ }
+ this.webview.send(messages.AUTOFILL_ADDRESSES_UPDATED, list)
+ }
+ if (this.props.autofillCreditCards) {
+ const creditCards = this.props.autofillCreditCards.toJS()
+ let list = []
+ for (let index in creditCards) {
+ const creditCard = currentWindow.webContents.session.autofill.getCreditCard(creditCards[index][partition])
+ let creditCardDetail = {
+ name: creditCard.name,
+ card: creditCard.card_number,
+ month: creditCard.expiration_month,
+ year: creditCard.expiration_year,
+ guid: creditCards[index]
+ }
+ list.push(creditCardDetail)
+ }
+ this.webview.send(messages.AUTOFILL_CREDIT_CARDS_UPDATED, list)
+ }
}
// send state to about pages
@@ -596,6 +635,16 @@ class Frame extends ImmutableComponent {
this.webview.addEventListener('page-title-updated', ({title}) => {
windowActions.setFrameTitle(this.frame, title)
})
+ this.webview.addEventListener('show-autofill-settings', (e) => {
+ windowActions.newFrame({ location: 'about:autofill' }, true)
+ })
+ this.webview.addEventListener('show-autofill-popup', (e) => {
+ contextMenus.onShowAutofillMenu(e.suggestions, e.rect, this.frame)
+ })
+ this.webview.addEventListener('hide-autofill-popup', (e) => {
+ // TODO(Anthony): conflict with contextmenu
+ // windowActions.setContextMenuDetail()
+ })
this.webview.addEventListener('ipc-message', (e) => {
let method = () => {}
switch (e.channel) {
@@ -655,6 +704,18 @@ class Frame extends ImmutableComponent {
method = (clearBrowsingDataDetail) =>
windowActions.setClearBrowsingDataDetail(clearBrowsingDataDetail)
break
+ case messages.ADD_AUTOFILL_ADDRESS:
+ windowActions.setAutofillAddressDetail({}, {})
+ break
+ case messages.EDIT_AUTOFILL_ADDRESS:
+ windowActions.setAutofillAddressDetail(e.args[0], e.args[0])
+ break
+ case messages.ADD_AUTOFILL_CREDIT_CARD:
+ windowActions.setAutofillCreditCardDetail({month: '1', year: new Date().getFullYear().toString()}, {})
+ break
+ case messages.EDIT_AUTOFILL_CREDIT_CARD:
+ windowActions.setAutofillCreditCardDetail(e.args[0], e.args[0])
+ break
}
method.apply(this, e.args)
})
diff --git a/js/components/main.js b/js/components/main.js
index b49ccf150a6..d4821c13baa 100644
--- a/js/components/main.js
+++ b/js/components/main.js
@@ -27,6 +27,8 @@ const Button = require('./button')
const SiteInfo = require('./siteInfo')
const BraveryPanel = require('./braveryPanel')
const ClearBrowsingDataPanel = require('./clearBrowsingDataPanel')
+const AutofillAddressPanel = require('./autofillAddressPanel')
+const AutofillCreditCardPanel = require('./autofillCreditCardPanel')
const AddEditBookmark = require('./addEditBookmark')
const LoginRequired = require('./loginRequired')
const ReleaseNotes = require('./releaseNotes')
@@ -77,6 +79,8 @@ class Main extends ImmutableComponent {
this.onHideSiteInfo = this.onHideSiteInfo.bind(this)
this.onHideBraveryPanel = this.onHideBraveryPanel.bind(this)
this.onHideClearBrowsingDataPanel = this.onHideClearBrowsingDataPanel.bind(this)
+ this.onHideAutofillAddressPanel = this.onHideAutofillAddressPanel.bind(this)
+ this.onHideAutofillCreditCardPanel = this.onHideAutofillCreditCardPanel.bind(this)
this.onHideNoScript = this.onHideNoScript.bind(this)
this.onHideReleaseNotes = this.onHideReleaseNotes.bind(this)
this.onBraveMenu = this.onBraveMenu.bind(this)
@@ -537,6 +541,14 @@ class Main extends ImmutableComponent {
windowActions.setClearBrowsingDataDetail()
}
+ onHideAutofillAddressPanel () {
+ windowActions.setAutofillAddressDetail()
+ }
+
+ onHideAutofillCreditCardPanel () {
+ windowActions.setAutofillCreditCardDetail()
+ }
+
onHideNoScript () {
windowActions.setNoScriptVisible(false)
}
@@ -683,6 +695,8 @@ class Main extends ImmutableComponent {
const braveShieldsDisabled = this.braveShieldsDisabled
const braveryPanelIsVisible = !braveShieldsDisabled && this.props.windowState.get('braveryPanelDetail')
const clearBrowsingDataPanelIsVisible = this.props.windowState.get('clearBrowsingDataDetail')
+ const autofillAddressPanelIsVisible = this.props.windowState.get('autofillAddressDetail')
+ const autofillCreditCardPanelIsVisible = this.props.windowState.get('autofillCreditCardDetail')
const activeRequestedLocation = this.activeRequestedLocation
const noScriptIsVisible = this.props.windowState.getIn(['ui', 'noScriptInfo', 'isVisible'])
const releaseNotesIsVisible = this.props.windowState.getIn(['ui', 'releaseNotes', 'isVisible'])
@@ -693,6 +707,8 @@ class Main extends ImmutableComponent {
!siteInfoIsVisible &&
!braveryPanelIsVisible &&
!clearBrowsingDataPanelIsVisible &&
+ !autofillAddressPanelIsVisible &&
+ !autofillCreditCardPanelIsVisible &&
!releaseNotesIsVisible &&
!noScriptIsVisible &&
activeFrame && !activeFrame.getIn(['security', 'loginRequiredDetail'])
@@ -784,6 +800,22 @@ class Main extends ImmutableComponent {
onHide={this.onHideClearBrowsingDataPanel} />
: null
}
+ {
+ autofillAddressPanelIsVisible
+ ?
+ : null
+ }
+ {
+ autofillCreditCardPanelIsVisible
+ ?
+ : null
+ }
{
activeFrame && activeFrame.getIn(['security', 'loginRequiredDetail'])
?
@@ -937,6 +969,8 @@ class Main extends ImmutableComponent {
enableNoScript={this.enableNoScript(this.frameSiteSettings(frame.get('location')))}
isPreview={frame.get('key') === this.props.windowState.get('previewFrameKey')}
isActive={FrameStateUtil.isFrameKeyActive(this.props.windowState, frame.get('key'))}
+ autofillCreditCards={this.props.appState.getIn(['autofill', 'creditCards'])}
+ autofillAddresses={this.props.appState.getIn(['autofill', 'addresses'])}
/>)
}
diff --git a/js/constants/appConfig.js b/js/constants/appConfig.js
index 4a78622c32b..50b18a309b2 100644
--- a/js/constants/appConfig.js
+++ b/js/constants/appConfig.js
@@ -103,6 +103,7 @@ module.exports = {
'payments.enabled': false,
'payments.notifications': false,
'payments.contribution-amount': 5, // USD
+ 'privacy.autofill-enabled': false,
'privacy.do-not-track': false,
'security.passwords.active-password-manager': null, // Set in settings.js by passwordManagerDefault (defaults to built in)
'security.passwords.manager-enabled': true,
diff --git a/js/constants/appConstants.js b/js/constants/appConstants.js
index 652aa127cf0..9f85b089770 100644
--- a/js/constants/appConstants.js
+++ b/js/constants/appConstants.js
@@ -35,7 +35,11 @@ const AppConstants = {
APP_CLEAR_MESSAGE_BOXES: _, /** @param {string} origin */
APP_ADD_WORD: _, /** @param {string} word, @param {boolean} learn */
APP_SET_DICTIONARY: _, /** @param {string} locale */
- APP_SET_BITCOIN_HANDLED: _ /** @param {boolean} handled */
+ APP_SET_BITCOIN_HANDLED: _, /** @param {boolean} handled */
+ APP_ADD_AUTOFILL_ADDRESS: _,
+ APP_REMOVE_AUTOFILL_ADDRESS: _,
+ APP_ADD_AUTOFILL_CREDIT_CARD: _,
+ APP_REMOVE_AUTOFILL_CREDIT_CARD: _
}
module.exports = mapValuesByKeys(AppConstants)
diff --git a/js/constants/messages.js b/js/constants/messages.js
index 2f9e969cd82..36e39f8ce88 100644
--- a/js/constants/messages.js
+++ b/js/constants/messages.js
@@ -137,6 +137,15 @@ const messages = {
CHECK_FLASH_INSTALLED: _,
ABOUT_COMPONENT_INITIALIZED: _,
CLEAR_BROWSING_DATA_NOW: _,
+ // Autofill
+ ADD_AUTOFILL_ADDRESS: _,
+ REMOVE_AUTOFILL_ADDRESS: _,
+ EDIT_AUTOFILL_ADDRESS: _,
+ ADD_AUTOFILL_CREDIT_CARD: _,
+ REMOVE_AUTOFILL_CREDIT_CARD: _,
+ EDIT_AUTOFILL_CREDIT_CARD: _,
+ AUTOFILL_ADDRESSES_UPDATED: _,
+ AUTOFILL_CREDIT_CARDS_UPDATED: _,
// HTTPS
CERT_ERROR_ACCEPTED: _, /** @arg {string} url where a cert error was accepted */
CHECK_CERT_ERROR_ACCEPTED: _, /** @arg {string} url to check cert error, @arg {number} key of frame */
diff --git a/js/constants/settings.js b/js/constants/settings.js
index ec01177bcc6..7073b398a6b 100644
--- a/js/constants/settings.js
+++ b/js/constants/settings.js
@@ -32,6 +32,8 @@ const settings = {
SHUTDOWN_CLEAR_DOWNLOADS: 'shutdown.clear-downloads',
SHUTDOWN_CLEAR_CACHE: 'shutdown.clear-cache',
SHUTDOWN_CLEAR_ALL_SITE_COOKIES: 'shutdown.clear-all-site-cookies',
+ // Autofill
+ AUTOFILL_ENABLED: 'privacy.autofill-enabled',
// Security Tab: DEPRECATED but still required (for now)
PASSWORD_MANAGER_ENABLED: 'security.passwords.manager-enabled',
ONE_PASSWORD_ENABLED: 'security.passwords.one-password-enabled',
diff --git a/js/constants/windowConstants.js b/js/constants/windowConstants.js
index fa4db57ab3f..0b1a02e8d74 100644
--- a/js/constants/windowConstants.js
+++ b/js/constants/windowConstants.js
@@ -65,7 +65,9 @@ const windowConstants = {
WINDOW_SET_LOGIN_REQUIRED_DETAIL: _,
WINDOW_SET_STATE: _,
WINDOW_SET_LAST_ZOOM_PERCENTAGE: _,
- WINDOW_SET_CLEAR_BROWSING_DATA_DETAIL: _
+ WINDOW_SET_CLEAR_BROWSING_DATA_DETAIL: _,
+ WINDOW_SET_AUTOFILL_ADDRESS_DETAIL: _,
+ WINDOW_SET_AUTOFILL_CREDIT_CARD_DETAIL: _
}
module.exports = mapValuesByKeys(windowConstants)
diff --git a/js/contextMenus.js b/js/contextMenus.js
index 01866344755..e8d3843e6ac 100644
--- a/js/contextMenus.js
+++ b/js/contextMenus.js
@@ -374,6 +374,36 @@ function usernameTemplateInit (usernames, origin, action) {
return items
}
+function autofillTemplateInit (suggestions, frame) {
+ let items = []
+ for (let i = 0; i < suggestions.length; ++i) {
+ let value
+ let frontendId = suggestions[i].frontend_id
+ console.log(frontendId)
+ if (frontendId >= 0) { // POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY and Autofill Entry
+ value = suggestions[i].value
+ } else if (frontendId === -1) { // POPUP_ITEM_ID_WARNING_MESSAGE
+ value = 'Disabled due to unsecure connection.'
+ } else if (frontendId === -4) { // POPUP_ITEM_ID_CLEAR_FORM
+ value = 'Clear Form'
+ } else if (frontendId === -5) { // POPUP_ITEM_ID_AUTOFILL_OPTIONS
+ value = 'Autofill Settings'
+ }
+ if (frontendId === -3) { // POPUP_ITEM_ID_SEPARATOR
+ items.push(CommonMenu.separatorMenuItem)
+ } else {
+ items.push({
+ label: value,
+ click: (item, focusedWindow) => {
+ ipc.send('autofill-selection-clicked', frame.get('tabId'), value, frontendId, i)
+ windowActions.setContextMenuDetail()
+ }
+ })
+ }
+ }
+ return items
+}
+
function tabTemplateInit (frameProps) {
const frameKey = frameProps.get('key')
const items = []
@@ -1059,6 +1089,15 @@ function onShowUsernameMenu (usernames, origin, action, boundingRect,
}))
}
+function onShowAutofillMenu (suggestions, boundingRect, frame) {
+ const menuTemplate = autofillTemplateInit(suggestions, frame)
+ windowActions.setContextMenuDetail(Immutable.fromJS({
+ left: boundingRect.x,
+ top: boundingRect.y,
+ template: menuTemplate
+ }))
+}
+
function onMoreBookmarksMenu (activeFrame, allBookmarkItems, overflowItems, e) {
const menuTemplate = moreBookmarksTemplateInit(allBookmarkItems, overflowItems, activeFrame)
const rect = e.target.getBoundingClientRect()
@@ -1166,6 +1205,7 @@ module.exports = {
onSiteDetailContextMenu,
onShowBookmarkFolderMenu,
onShowUsernameMenu,
+ onShowAutofillMenu,
onMoreBookmarksMenu,
onBackButtonHistoryMenu,
onForwardButtonHistoryMenu
diff --git a/js/lib/appUrlUtil.js b/js/lib/appUrlUtil.js
index da24c34e1ba..285660cbce6 100644
--- a/js/lib/appUrlUtil.js
+++ b/js/lib/appUrlUtil.js
@@ -68,7 +68,8 @@ module.exports.aboutUrls = new Immutable.Map({
'about:safebrowsing': module.exports.getAppUrl('about-safebrowsing.html'),
'about:passwords': module.exports.getAppUrl('about-passwords.html'),
'about:flash': module.exports.getAppUrl('about-flash.html'),
- 'about:error': module.exports.getAppUrl('about-error.html')
+ 'about:error': module.exports.getAppUrl('about-error.html'),
+ 'about:autofill': module.exports.getAppUrl('about-autofill.html')
})
module.exports.isIntermediateAboutPage = (location) =>
diff --git a/js/state/privacy.js b/js/state/privacy.js
new file mode 100644
index 00000000000..89abe7d4fa7
--- /dev/null
+++ b/js/state/privacy.js
@@ -0,0 +1,34 @@
+/* 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/. */
+
+const AppDispatcher = require('../dispatcher/appDispatcher')
+const AppStore = require('../stores/appStore')
+const AppConstants = require('../constants/appConstants')
+const settings = require('../constants/settings')
+const { registerUserPrefs } = require('./userPrefs')
+
+const getAutofillEnabled = (appState) => {
+ let appSettings = appState.get('settings')
+ return appSettings.get(settings.AUTOFILL_ENABLED)
+}
+
+const getPrivacySettings = (appState) => {
+ return { 'autofill.enabled': getAutofillEnabled(appState) }
+}
+
+let updateTrigger
+
+// Register callback to handle all updates
+const doAction = (action) => {
+ if (action.actionType === AppConstants.APP_CHANGE_SETTING) {
+ AppDispatcher.waitFor([AppStore.dispatchToken], () => {
+ updateTrigger()
+ })
+ }
+}
+
+module.exports.init = () => {
+ updateTrigger = registerUserPrefs(() => getPrivacySettings(AppStore.getState()))
+ AppDispatcher.register(doAction)
+}
diff --git a/js/stores/appStore.js b/js/stores/appStore.js
index f69349aff20..d816bbb34b5 100644
--- a/js/stores/appStore.js
+++ b/js/stores/appStore.js
@@ -545,6 +545,64 @@ const handleAppAction = (action) => {
case AppConstants.APP_SET_BITCOIN_HANDLED:
appState = appState.set('hasBitcoinHandler', action.handled)
break
+ case AppConstants.APP_ADD_AUTOFILL_ADDRESS:
+ {
+ const Filtering = require('../../app/filtering')
+ if (appState.getIn(['autofill', 'addresses']) === undefined) {
+ appState = appState.setIn(['autofill', 'addresses'], new Immutable.List())
+ }
+ appState = appState.setIn(['autofill', 'addresses'],
+ appState.getIn(['autofill', 'addresses']).filterNot((address) => {
+ return Immutable.is(address, action.originalDetail.get('guid'))
+ }))
+ if (action.originalDetail.get('guid') !== undefined) {
+ Filtering.removeAutofillAddress(action.originalDetail.get('guid').toJS())
+ }
+
+ let addresses = appState.getIn(['autofill', 'addresses'])
+ const guid = Filtering.addAutofillAddress(action.detail.toJS())
+ appState = appState.setIn(['autofill', 'addresses'], addresses.push(Immutable.fromJS(guid)))
+ break
+ }
+ case AppConstants.APP_REMOVE_AUTOFILL_ADDRESS:
+ {
+ const Filtering = require('../../app/filtering')
+ appState = appState.setIn(['autofill', 'addresses'],
+ appState.getIn(['autofill', 'addresses']).filterNot((address) => {
+ return Immutable.is(address, Immutable.fromJS(action.detail.guid))
+ }))
+ Filtering.removeAutofillAddress(action.detail.guid)
+ break
+ }
+ case AppConstants.APP_ADD_AUTOFILL_CREDIT_CARD:
+ {
+ const Filtering = require('../../app/filtering')
+ if (appState.getIn(['autofill', 'creditCards']) === undefined) {
+ appState = appState.setIn(['autofill', 'creditCards'], new Immutable.List())
+ }
+ appState = appState.setIn(['autofill', 'creditCards'],
+ appState.getIn(['autofill', 'creditCards']).filterNot((card) => {
+ return Immutable.is(card, action.originalDetail.get('guid'))
+ }))
+ if (action.originalDetail.get('guid') !== undefined) {
+ Filtering.removeAutofillCreditCard(action.originalDetail.get('guid').toJS())
+ }
+
+ let creditCards = appState.getIn(['autofill', 'creditCards'])
+ const guid = Filtering.addAutofillCreditCard(action.detail.toJS())
+ appState = appState.setIn(['autofill', 'creditCards'], creditCards.push(Immutable.fromJS(guid)))
+ break
+ }
+ case AppConstants.APP_REMOVE_AUTOFILL_CREDIT_CARD:
+ {
+ const Filtering = require('../../app/filtering')
+ appState = appState.setIn(['autofill', 'creditCards'],
+ appState.getIn(['autofill', 'creditCards']).filterNot((card) => {
+ return Immutable.is(card, Immutable.fromJS(action.detail.guid))
+ }))
+ Filtering.removeAutofillCreditCard(action.detail.guid)
+ break
+ }
default:
}
diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js
index 7db1c3c1af0..acc9585c75c 100644
--- a/js/stores/windowStore.js
+++ b/js/stores/windowStore.js
@@ -704,6 +704,30 @@ const doAction = (action) => {
windowState = windowState.set('clearBrowsingDataDetail', Immutable.fromJS(action.clearBrowsingDataDetail))
}
break
+ case WindowConstants.WINDOW_SET_AUTOFILL_ADDRESS_DETAIL:
+ if (!action.currentDetail && !action.originalDetail) {
+ windowState = windowState.delete('autofillAddressDetail')
+ } else {
+ windowState = windowState.mergeIn(['autofillAddressDetail'], {
+ currentDetail: action.currentDetail,
+ originalDetail: action.originalDetail
+ })
+ }
+ // Since the input values of address are bound, we need to notify the controls sync.
+ windowStore.emitChanges()
+ break
+ case WindowConstants.WINDOW_SET_AUTOFILL_CREDIT_CARD_DETAIL:
+ if (!action.currentDetail && !action.originalDetail) {
+ windowState = windowState.delete('autofillCreditCardDetail')
+ } else {
+ windowState = windowState.mergeIn(['autofillCreditCardDetail'], {
+ currentDetail: action.currentDetail,
+ originalDetail: action.originalDetail
+ })
+ }
+ // Since the input values of credit card are bound, we need to notify the controls sync.
+ windowStore.emitChanges()
+ break
case WindowConstants.WINDOW_SET_DOWNLOADS_TOOLBAR_VISIBLE:
windowState = windowState.setIn(['ui', 'downloadsToolbar', 'isVisible'], action.isVisible)
break
diff --git a/less/about/autofill.less b/less/about/autofill.less
new file mode 100644
index 00000000000..65f47015f92
--- /dev/null
+++ b/less/about/autofill.less
@@ -0,0 +1,59 @@
+@import "./common.less";
+
+.autofillPage {
+ margin: 20px;
+
+ .autofillPageContent {
+ border-top: 1px solid @chromeBorderColor;
+
+ .autofillList {
+ padding-top: 10px;
+ overflow: hidden;
+ }
+ }
+}
+
+th {
+ padding: 8px;
+ text-align: left;
+}
+
+tr {
+ cursor: default;
+ overflow: hidden;
+ white-space: nowrap;
+
+ padding: 12px;
+ -webkit-user-select: none;
+
+ .autofillItem {
+ display: flex;
+ }
+
+ &:hover :not(th) {
+ background-color: lighten(@highlightBlue, 30%);
+ }
+}
+
+td:not(.autofillActions) {
+ padding-right: 8px;
+ padding-left: 8px;
+}
+
+.autofillAction {
+ cursor: pointer;
+ padding: 8px;
+ &:hover {
+ background-color: lighten(@highlightBlue, 20%);
+ }
+}
+
+.autofillPageFooter {
+ padding: 10px;
+ margin-bottom: 20px;
+ span {
+ color: grey;
+ cursor: pointer;
+ text-decoration: underline;
+ }
+}
diff --git a/less/about/preferences.less b/less/about/preferences.less
index f244b34571e..39f5f72ca88 100644
--- a/less/about/preferences.less
+++ b/less/about/preferences.less
@@ -295,6 +295,12 @@ span.browserButton.primaryButton.clearBrowsingDataButton {
margin-top: 20px;
}
+span.browserButton.primaryButton.manageAutofillDataButton {
+ font-size: 0.9em;
+ padding: 5px 20px;
+ margin-top: 20px;
+}
+
.settingsList {
.settingItem {
diff --git a/less/forms.less b/less/forms.less
index b1ffc72421e..d986467b506 100644
--- a/less/forms.less
+++ b/less/forms.less
@@ -99,6 +99,39 @@
}
}
+.manageAutofillDataPanel {
+ .manageAutofillData {
+ .flyoutDialog;
+ background-color: #f7f7f7;
+ border-radius: @borderRadius;
+ max-width: 450px;
+ padding: 10px;
+ text-align: left;
+ width: 473px;
+ -webkit-user-select: none;
+ cursor: default;
+ color: #3B3B3B;
+ overflow-y: auto;
+ max-height: 100%;
+
+ .clickable {
+ color: #5B5B5B;
+ &:hover {
+ color: #000;
+ }
+ }
+
+ .manageAutofillDataTitle {
+ color: #ff5000;
+ font-size: 1.2em;
+ }
+
+ .formSelect {
+ width: 5.5em
+ }
+ }
+}
+
.braveryPanelContainer {
.braveryPanel {
diff --git a/test/components/autofillTest.js b/test/components/autofillTest.js
new file mode 100644
index 00000000000..4eb18482128
--- /dev/null
+++ b/test/components/autofillTest.js
@@ -0,0 +1,317 @@
+/* global describe, it, before */
+
+const Brave = require('../lib/brave')
+const assert = require('assert')
+const messages = require('../../js/constants/messages')
+const {urlInput, autofillAddressPanel, autofillCreditCardPanel} = require('../lib/selectors')
+const {getTargetAboutUrl} = require('../../js/lib/appUrlUtil')
+
+describe('Autofill', function () {
+ function * setup (client) {
+ yield client
+ .waitUntilWindowLoaded()
+ .waitForUrl(Brave.newTabUrl)
+ .waitForBrowserWindow()
+ .waitForVisible('#window')
+ .waitForVisible(urlInput)
+ }
+
+ describe('Data Management', function () {
+ Brave.beforeAll(this)
+ before(function * () {
+ yield setup(this.app.client)
+ })
+ const page1Url = 'about:autofill'
+ const addAddressButton = '.addAddressButton'
+ const saveAddressButton = '.saveAddressButton'
+ const name = 'Brave Lion'
+ const organization = 'Brave'
+ const streetAddress = '1161 Mission Street, #401'
+ const city = 'San Francisco'
+ const state = 'CA'
+ const postalCode = '94103-1550'
+ const country = 'US'
+ const phone = '0987654321'
+ const email = 'press@brave.com'
+ it('Adding Address', function * () {
+ yield this.app.client
+ .tabByIndex(0)
+ .loadUrl(page1Url)
+ .url(getTargetAboutUrl(page1Url))
+ .waitForVisible(addAddressButton)
+ .click(addAddressButton)
+ .windowByUrl(Brave.browserWindowUrl)
+ .waitForVisible(autofillAddressPanel)
+ .click('#nameOnAddress')
+ .keys(name)
+ .click('#organization')
+ .keys(organization)
+ .click('#streetAddress')
+ .keys(streetAddress)
+ .click('#city')
+ .keys(city)
+ .click('#state')
+ .keys(state)
+ .click('#postalCode')
+ .keys(postalCode)
+ .click('#country')
+ .keys(country)
+ .click('#phone')
+ .keys(phone)
+ .click('#email')
+ .keys(email)
+ .click(saveAddressButton)
+ .waitUntil(function () {
+ return this.getAppState().then((val) => {
+ return val.value.autofill.addresses.length === 1
+ })
+ })
+ .tabByUrl(this.page1Url)
+ .waitForVisible('.autofillPage')
+ .getText('.addressName').should.eventually.be.equal(name)
+ .getText('.organization').should.eventually.be.equal(organization)
+ .getText('.streetAddress').should.eventually.be.equal(streetAddress)
+ .getText('.city').should.eventually.be.equal(city)
+ .getText('.state').should.eventually.be.equal(state)
+ .getText('.postalCode').should.eventually.be.equal(postalCode)
+ .getText('.country').should.eventually.be.equal(country)
+ .getText('.phone').should.eventually.be.equal(phone)
+ .getText('.email').should.eventually.be.equal(email)
+ })
+ it('Address form test', function * () {
+ const page1Url = 'https://www.roboform.com/filling-test-all-fields'
+ yield this.app.client
+ .tabByIndex(0)
+ .loadUrl(page1Url)
+ .url(getTargetAboutUrl(page1Url))
+ .waitForVisible('