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 } +
+ + +
+
+ + + } +} + +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('
') + .click('[name="04fullname"]') + .click('[name="04fullname"]') + .windowByUrl(Brave.browserWindowUrl) + .ipcSendRenderer('autofill-selection-clicked', 2, name, 1, 0) + .setContextMenuDetail() + .tabByUrl(this.page1Url) + .getValue('[name="04fullname"]').should.eventually.be.equal(name) + }) + it('Editing Address', function * () { + yield this.app.client + .tabByIndex(0) + .loadUrl(page1Url) + .url(getTargetAboutUrl(page1Url)) + .waitForVisible('[title="Edit address"]') + .click('[title="Edit address"]') + .windowByUrl(Brave.browserWindowUrl) + .waitForVisible(autofillAddressPanel) + .click('#phone') + .keys('123') + .click('#email') + .keys('mm') + .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 + '123') + .getText('.email').should.eventually.be.equal(email + 'mm') + }) + it('Edited 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('') + .click('[name="04fullname"]') + .click('[name="04fullname"]') + .windowByUrl(Brave.browserWindowUrl) + .ipcSendRenderer('autofill-selection-clicked', 2, name, 1, 0) + .setContextMenuDetail() + .tabByUrl(this.page1Url) + .getValue('[name="04fullname"]').should.eventually.be.equal(name) + .getValue('[name="23cellphon"]').should.eventually.be.equal(phone + '123') + .getValue('[name="24emailadr"]').should.eventually.be.equal(email + 'mm') + }) + it('Deleting Address', function * () { + yield this.app.client + .tabByIndex(0) + .loadUrl(page1Url) + .url(getTargetAboutUrl(page1Url)) + .waitForVisible('[title="Delete address"]') + .click('[title="Delete address"]') + .waitForVisible('[data-l10n-id=noAddressesSaved]') + }) + const addCreditCardButton = '.addCreditCardButton' + const saveCreditCardButton = '.saveCreditCardButton' + const cardName = 'Test Card' + const cardNumber = '1234567890' + const expMonth = 10 + const expYear = new Date().getFullYear() + 2 + it('Adding Credit Card', function * () { + yield this.app.client + .tabByIndex(0) + .loadUrl(page1Url) + .url(getTargetAboutUrl(page1Url)) + .waitForVisible(addCreditCardButton) + .click(addCreditCardButton) + .windowByUrl(Brave.browserWindowUrl) + .waitForVisible(autofillCreditCardPanel) + .click('#nameOnCard') + .keys(cardName) + .click('#creditCardNumber') + .keys(cardNumber) + .selectByValue('.expMonthSelect', expMonth.toString()) + .selectByValue('.expYearSelect', expYear.toString()) + .click(saveCreditCardButton) + .waitUntil(function () { + return this.getAppState().then((val) => { + return val.value.autofill.creditCards.length === 1 + }) + }) + .tabByUrl(this.page1Url) + .waitForVisible('.autofillPage') + .getText('.creditCardName').should.eventually.be.equal(cardName) + .getText('.creditCardNumber').should.eventually.be.equal('***' + cardNumber.slice(-4)) + .getText('.creditCardPExpirationDate').should.eventually.be.equal(expMonth.toString() + '/' + expYear.toString()) + }) + it('Credit Card 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('') + .click('[name="41ccnumber"]') + .click('[name="41ccnumber"]') + .windowByUrl(Brave.browserWindowUrl) + .ipcSendRenderer('autofill-selection-clicked', 2, cardNumber, 65536, 0) + .setContextMenuDetail() + .tabByUrl(this.page1Url) + .getValue('[name="41ccnumber"]').should.eventually.be.equal(cardNumber) + .getValue('[name="42ccexp_mm"]').should.eventually.be.equal(expMonth.toString()) + .getValue('[name="43ccexp_yy"]').should.eventually.be.equal(expYear.toString()) + }) + it('Editing Credit Card', function * () { + yield this.app.client + .tabByIndex(0) + .loadUrl(page1Url) + .url(getTargetAboutUrl(page1Url)) + .waitForVisible('[title="Edit creditCard"]') + .click('[title="Edit creditCard"]') + .windowByUrl(Brave.browserWindowUrl) + .waitForVisible(autofillCreditCardPanel) + .click('#nameOnCard') + .keys('123') + .click('#creditCardNumber') + .keys('123') + .selectByValue('.expMonthSelect', (expMonth + 1).toString()) + .selectByValue('.expYearSelect', (expYear + 1).toString()) + .click(saveCreditCardButton) + .waitUntil(function () { + return this.getAppState().then((val) => { + return val.value.autofill.creditCards.length === 1 + }) + }) + .tabByUrl(this.page1Url) + .waitForVisible('.autofillPage') + .getText('.creditCardName').should.eventually.be.equal(cardName + 123) + .getText('.creditCardNumber').should.eventually.be.equal('***' + (cardNumber + 123).slice(-4)) + .getText('.creditCardPExpirationDate').should.eventually.be.equal( + (expMonth + 1).toString() + '/' + (expYear + 1).toString()) + }) + it('Edited Credit Card 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('') + .click('[name="41ccnumber"]') + .click('[name="41ccnumber"]') + .windowByUrl(Brave.browserWindowUrl) + .ipcSendRenderer('autofill-selection-clicked', 2, cardNumber, 65536, 0) + .setContextMenuDetail() + .tabByUrl(this.page1Url) + .getValue('[name="41ccnumber"]').should.eventually.be.equal(cardNumber + '123') + .getValue('[name="42ccexp_mm"]').should.eventually.be.equal((expMonth + 1).toString()) + .getValue('[name="43ccexp_yy"]').should.eventually.be.equal((expYear + 1).toString()) + }) + it('Deleting Credit Card', function * () { + yield this.app.client + .tabByIndex(0) + .loadUrl(page1Url) + .url(getTargetAboutUrl(page1Url)) + .waitForVisible('[title="Delete creditCard"]') + .click('[title="Delete creditCard"]') + .waitForVisible('[data-l10n-id=noCreditCardsSaved]') + }) + }) + + describe('prevent autocomplete data leak from private to regular', function () { + Brave.beforeAll(this) + const url = 'https://yoast.com/research/autocompletetype.php' + before(function * () { + yield setup(this.app.client) + }) + it('submit form on regular', function * () { + yield this.app.client + .ipcSend(messages.SHORTCUT_NEW_FRAME, url) + .waitForUrl(url) + .windowByUrl(Brave.browserWindowUrl) + .waitForExist('.tab[data-frame-key="2"]') + .waitForVisible('webview[partition="persist:default"]') + .tabByUrl(this.url) + .setValue('[x-autocompletetype="name"]', 'bravery') + .click('[value="Submit your name"]') + .loadUrl(url) + .click('[x-autocompletetype="name"]') + .keys('b') + .windowByUrl(Brave.browserWindowUrl) + let item = yield this.app.client.getText('.contextMenuItemText') + assert.equal(item, 'bravery') + yield this.app.client.setContextMenuDetail() + }) + it('submit form on private', function * () { + yield this.app.client + .ipcSend(messages.SHORTCUT_NEW_FRAME, url, { isPrivate: true }) + .waitForUrl(url) + .windowByUrl(Brave.browserWindowUrl) + .waitForExist('.tab.private[data-frame-key="3"]') + .waitForVisible('webview[partition="default"]') + .tabByUrl(this.url) + .setValue('[x-autocompletetype="name"]', 'bravery2') + .click('[value="Submit your name"]') + .loadUrl(url) + .click('[x-autocompletetype="name"]') + .keys('b') + .windowByUrl(Brave.browserWindowUrl) + let item = yield this.app.client.getText('.contextMenuItemText') + assert.equal(item, 'bravery') + yield this.app.client.setContextMenuDetail() + }) + it('check on regular', function * () { + yield this.app.client + .ipcSend(messages.SHORTCUT_NEW_FRAME, url) + .waitForUrl(url) + .windowByUrl(Brave.browserWindowUrl) + .waitForExist('.tab[data-frame-key="4"]') + .waitForVisible('webview[partition="persist:default"]') + .tabByUrl(this.url) + .click('[x-autocompletetype="name"]') + .keys('b') + .windowByUrl(Brave.browserWindowUrl) + let item = yield this.app.client.getText('.contextMenuItemText') + assert.equal(item, 'bravery') + yield this.app.client.setContextMenuDetail() + }) + }) +}) diff --git a/test/lib/brave.js b/test/lib/brave.js index 48f0e13a12a..44c2b923404 100644 --- a/test/lib/brave.js +++ b/test/lib/brave.js @@ -106,6 +106,12 @@ var exports = { }, message, ...param).then((response) => response.value) }) + this.app.client.addCommand('ipcSendRenderer', function (message, ...param) { + return this.execute(function (message, ...param) { + return require('electron').ipcRenderer.send(message, ...param) + }, message, ...param).then((response) => response.value) + }) + var windowHandlesOrig = this.app.client.windowHandles Object.getPrototypeOf(this.app.client).windowHandles = function () { return windowHandlesOrig.apply(this) @@ -196,6 +202,12 @@ var exports = { }) }) + this.app.client.addCommand('setContextMenuDetail', function () { + return this.execute(function () { + return window.windowActions.setContextMenuDetail() + }) + }) + this.app.client.addCommand('showFindbar', function (show) { return this.execute(function (show) { window.windowActions.setFindbarShown(Object.assign({ diff --git a/test/lib/selectors.js b/test/lib/selectors.js index dd19392bba5..c536b6281a8 100644 --- a/test/lib/selectors.js +++ b/test/lib/selectors.js @@ -45,5 +45,8 @@ module.exports = { clearBrowsingDataPanel: '.clearBrowsingDataPanel', clearBrowsingDataButton: '.clearBrowsingDataButton', saveButton: '[data-l10n-id="save"]', - securityTab: '[data-l10n-id="security"]' + securityTab: '[data-l10n-id="security"]', + saveButton: '[data-l10n-id="save"]', + autofillAddressPanel: '.autofillAddressPanel', + autofillCreditCardPanel: '.autofillCreditCardPanel' }