From 9b38ba93dc96fbbb809cdf92329827e2c089ac67 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Wed, 9 Jun 2021 09:06:38 +0200 Subject: [PATCH 01/64] Add pm API endpoints Signed-off-by: Emanuele Feliziani --- dist/autofill.js | 42 +++++++++++++++++++++++++++++++++++++ src/DeviceInterface.js | 47 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/dist/autofill.js b/dist/autofill.js index ba0c6d647..fc251685a 100644 --- a/dist/autofill.js +++ b/dist/autofill.js @@ -470,6 +470,48 @@ class AppleDeviceInterface extends InterfacePrototype { }); this.attachTooltip = createAttachTooltip(this.getAlias, this.refreshAlias); + /** + * PM endpoints + */ + + /** + * @typedef {{ + * id: Number + * username: String + * password?: String + * lastUpdated: String + * }} CredentialsObject + */ + + /** + * Sends credentials to the native layer + * @param {{username: String, password: String}} credentials + */ + + this.storeCredentials = credentials => wkSend('pmHandlerStoreCredentials', credentials); + /** + * Gets a list of credentials for the current site + * @returns {Promise<{ success: [CredentialsObject], error?: String }>} + */ + + + this.getCredentials = () => wkSendAndWait('pmHandlerGetCredentials').then(response => { + console.log('rattone', response); + return response; + }); + /** + * Gets credentials ready for autofill + * @param {Number} id - the credential id + * @returns {Promise<{ success: CredentialsObject, error?: String }>} + */ + + + this.getAutofillCredentials = id => wkSendAndWait('pmHandlerGetAutofillCredentials', { + id + }).then(response => { + console.log(response); + return response; + }); } } diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index 07ebb66df..10228cc75 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -228,6 +228,53 @@ class AppleDeviceInterface extends InterfacePrototype { wkSend('emailHandlerStoreToken', { token, username: userName }) this.attachTooltip = createAttachTooltip(this.getAlias, this.refreshAlias) + + /** + * PM endpoints + */ + + /** + * @typedef {{ + * id: Number + * username: String + * password?: String + * lastUpdated: String + * }} CredentialsObject + */ + + /** + * Sends credentials to the native layer + * @param {{username: String, password: String}} credentials + */ + this.storeCredentials = (credentials) => + wkSend('pmHandlerStoreCredentials', credentials) + + /** + * Gets a list of credentials for the current site + * @returns {Promise<{ success: [CredentialsObject], error?: String }>} + */ + this.getCredentials = () => wkSendAndWait('pmHandlerGetCredentials') + .then((response) => { + console.log(response) + return response + }) + + /** + * Gets credentials ready for autofill + * @param {Number} id - the credential id + * @returns {Promise<{ success: CredentialsObject, error?: String }>} + */ + this.getAutofillCredentials = (id) => + wkSendAndWait('pmHandlerGetAutofillCredentials', {id}) + .then((response) => { + console.log(response) + return response + }) + + /** + * Opens the native UI for managing passwords + */ + this.openManagePasswords = () => wkSend('pmHandlerOpenManagePasswords') } } From 7b61141be121524e60e9c3efe445b257f4aa97cf Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Wed, 9 Jun 2021 13:02:04 +0200 Subject: [PATCH 02/64] Move relevant files into the Form folder Signed-off-by: Emanuele Feliziani --- src/{ => Form}/Form.js | 15 +++++++++++---- src/{ => Form}/FormAnalyzer.js | 2 ++ src/{ => Form}/logo-svg.js | 0 src/scanForInputs.js | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) rename src/{ => Form}/Form.js (92%) rename src/{ => Form}/FormAnalyzer.js (99%) rename src/{ => Form}/logo-svg.js (100%) diff --git a/src/Form.js b/src/Form/Form.js similarity index 92% rename from src/Form.js rename to src/Form/Form.js index 9a9b6e905..8bea24603 100644 --- a/src/Form.js +++ b/src/Form/Form.js @@ -1,5 +1,5 @@ const FormAnalyzer = require('./FormAnalyzer') -const {addInlineStyles, removeInlineStyles, isDDGApp, isApp, setValue, isEventWithinDax} = require('./autofill-utils') +const {addInlineStyles, removeInlineStyles, isDDGApp, isApp, setValue, isEventWithinDax} = require('../autofill-utils') const {daxBase64} = require('./logo-svg') // In Firefox web_accessible_resources could leak a unique user identifier, so we avoid it here @@ -25,7 +25,8 @@ class Form { this.form = form this.formAnalyzer = new FormAnalyzer(form, input) this.attachTooltip = attachTooltip - this.relevantInputs = new Set() + this.emailInputs = new Set() + this.passwordInputs = new Set() this.touched = new Set() this.listeners = new Set() this.addInput(input) @@ -80,11 +81,17 @@ class Form { } execOnInputs (fn) { - this.relevantInputs.forEach(fn) + this.emailInputs.forEach(fn) } addInput (input) { - this.relevantInputs.add(input) + if (input.type === 'password') { + console.log('password input found ') + this.passwordInputs.add(input) + } else { + this.emailInputs.add(input) + } + if (this.formAnalyzer.autofillSignal > 0) this.decorateInput(input) return this } diff --git a/src/FormAnalyzer.js b/src/Form/FormAnalyzer.js similarity index 99% rename from src/FormAnalyzer.js rename to src/Form/FormAnalyzer.js index 8bdaf70d1..9641e68e0 100644 --- a/src/FormAnalyzer.js +++ b/src/Form/FormAnalyzer.js @@ -60,6 +60,8 @@ class FormAnalyzer { } evaluateElAttributes (el, signalStrength = 3, isInput = false) { + if (el.nodeName === 'INPUT' && el.type === 'password') {} + Array.from(el.attributes).forEach(attr => { if (attr.name === 'style') return diff --git a/src/logo-svg.js b/src/Form/logo-svg.js similarity index 100% rename from src/logo-svg.js rename to src/Form/logo-svg.js diff --git a/src/scanForInputs.js b/src/scanForInputs.js index 71d3e67bd..009281ef8 100644 --- a/src/scanForInputs.js +++ b/src/scanForInputs.js @@ -1,4 +1,4 @@ -const Form = require('./Form') +const Form = require('./Form/Form') const {notifyWebApp} = require('./autofill-utils') // Accepts the DeviceInterface as an explicit dependency From 3ac0522510ab1d1522b8a04f5178f94c1255b97a Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Wed, 9 Jun 2021 15:10:06 +0200 Subject: [PATCH 03/64] Move selectors to dedicated file Signed-off-by: Emanuele Feliziani --- src/Form/selectors.js | 19 +++++++++++++++++++ src/scanForInputs.js | 17 +---------------- 2 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 src/Form/selectors.js diff --git a/src/Form/selectors.js b/src/Form/selectors.js new file mode 100644 index 000000000..6fab3615b --- /dev/null +++ b/src/Form/selectors.js @@ -0,0 +1,19 @@ +const EMAIL_SELECTOR = ` + input:not([type])[name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), + input[type=""][name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), + input[type=text][name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), + input:not([type])[id*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), + input:not([type])[placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), + input[type=""][id*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), + input[type=text][placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), + input[type=""][placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), + input:not([type])[placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), + input[type=email]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), + input[type=text][aria-label*=mail i], + input:not([type])[aria-label*=mail i], + input[type=text][placeholder*=mail i]:not([readonly]) +` + +const PASSWORD_SELECTOR = `input[type=password]:not([autocomplete*=cc]):not([autocomplete=one-time-code])` + +module.exports = {EMAIL_SELECTOR, PASSWORD_SELECTOR} diff --git a/src/scanForInputs.js b/src/scanForInputs.js index 009281ef8..ddea2c5c0 100644 --- a/src/scanForInputs.js +++ b/src/scanForInputs.js @@ -1,26 +1,11 @@ const Form = require('./Form/Form') const {notifyWebApp} = require('./autofill-utils') +const {EMAIL_SELECTOR, PASSWORD_SELECTOR} = require('./Form/selectors') // Accepts the DeviceInterface as an explicit dependency const scanForInputs = (DeviceInterface) => { const forms = new Map() - const EMAIL_SELECTOR = ` - input:not([type])[name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), - input[type=""][name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), - input[type=text][name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), - input:not([type])[id*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), - input:not([type])[placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), - input[type=""][id*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), - input[type=text][placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), - input[type=""][placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), - input:not([type])[placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), - input[type=email]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]), - input[type=text][aria-label*=mail i], - input:not([type])[aria-label*=mail i], - input[type=text][placeholder*=mail i]:not([readonly]) - ` - const addInput = input => { const parentForm = input.form From 9b17596a1a05edd628dc5f5b15eeab9e55d7afb1 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Wed, 9 Jun 2021 16:45:01 +0200 Subject: [PATCH 04/64] Move button selector to selectors.js Signed-off-by: Emanuele Feliziani --- src/Form/FormAnalyzer.js | 8 +++----- src/Form/selectors.js | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Form/FormAnalyzer.js b/src/Form/FormAnalyzer.js index 9641e68e0..da241337b 100644 --- a/src/Form/FormAnalyzer.js +++ b/src/Form/FormAnalyzer.js @@ -1,3 +1,5 @@ +const {PASSWORD_SELECTOR, SUBMIT_BUTTON_SELECTOR} = require('./selectors') + class FormAnalyzer { constructor (form, input) { this.form = form @@ -132,11 +134,7 @@ class FormAnalyzer { const string = this.getText(el) // check button contents - if ( - (this.elementIs(el, 'INPUT') && ['submit', 'button'].includes(el.type)) || - (this.elementIs(el, 'BUTTON') && el.type === 'submit') || - ((el.getAttribute('role') || '').toUpperCase() === 'BUTTON') - ) { + if (el.matches(SUBMIT_BUTTON_SELECTOR)) { this.updateSignal({string, strength: 2, signalType: `submit: ${string}`}) } // if a link points to relevant urls or contain contents outside the page… diff --git a/src/Form/selectors.js b/src/Form/selectors.js index 6fab3615b..66523f908 100644 --- a/src/Form/selectors.js +++ b/src/Form/selectors.js @@ -16,4 +16,6 @@ const EMAIL_SELECTOR = ` const PASSWORD_SELECTOR = `input[type=password]:not([autocomplete*=cc]):not([autocomplete=one-time-code])` -module.exports = {EMAIL_SELECTOR, PASSWORD_SELECTOR} +const SUBMIT_BUTTON_SELECTOR = 'input[type=submit], input[type=button], button[type=submit], [role=button]' + +module.exports = {EMAIL_SELECTOR, PASSWORD_SELECTOR, SUBMIT_BUTTON_SELECTOR} From 96d43957aea6ba2bab2992f91eab49dbd3d3aefe Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Thu, 10 Jun 2021 06:48:57 +0200 Subject: [PATCH 05/64] Add password field analysis to improve accuracy Signed-off-by: Emanuele Feliziani --- src/Form/FormAnalyzer.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Form/FormAnalyzer.js b/src/Form/FormAnalyzer.js index da241337b..ce2a03a01 100644 --- a/src/Form/FormAnalyzer.js +++ b/src/Form/FormAnalyzer.js @@ -62,7 +62,23 @@ class FormAnalyzer { } evaluateElAttributes (el, signalStrength = 3, isInput = false) { - if (el.nodeName === 'INPUT' && el.type === 'password') {} + if (el.matches(PASSWORD_SELECTOR)) { + // These are explicit signals by the web author, so we weigh them heavily + if (el.getAttribute('autocomplete')?.includes('current-password')) { + this.updateSignal({ + string: 'current-password', + strength: -20, + signalType: 'current-password' + }) + } + if (el.getAttribute('autocomplete')?.includes('new-password')) { + this.updateSignal({ + string: 'new-password', + strength: 20, + signalType: 'new-password' + }) + } + } Array.from(el.attributes).forEach(attr => { if (attr.name === 'style') return From fc6b38549e36c8c011a1797d5b7e10336993c504 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Thu, 10 Jun 2021 06:50:45 +0200 Subject: [PATCH 06/64] Add explicit isLogin and isSignup to FormAnalyzer Signed-off-by: Emanuele Feliziani --- src/Form/Form.js | 3 +-- src/Form/FormAnalyzer.js | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Form/Form.js b/src/Form/Form.js index 8bea24603..57e9f8d6f 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -86,13 +86,12 @@ class Form { addInput (input) { if (input.type === 'password') { - console.log('password input found ') this.passwordInputs.add(input) } else { this.emailInputs.add(input) + if (this.formAnalyzer.isSignup) this.decorateInput(input) } - if (this.formAnalyzer.autofillSignal > 0) this.decorateInput(input) return this } diff --git a/src/Form/FormAnalyzer.js b/src/Form/FormAnalyzer.js index ce2a03a01..88ef9fb5b 100644 --- a/src/Form/FormAnalyzer.js +++ b/src/Form/FormAnalyzer.js @@ -14,6 +14,14 @@ class FormAnalyzer { return this } + get isLogin () { + return this.autofillSignal < 0 + } + + get isSignup () { + return this.autofillSignal > 0 + } + increaseSignalBy (strength, signal) { this.autofillSignal += strength this.signals.push(`${signal}: +${strength}`) From 9c25183b3a4ba69faf77fd396e25fcc03b799c30 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 05:45:31 +0200 Subject: [PATCH 07/64] Add support for private class members Signed-off-by: Emanuele Feliziani --- .babelrc | 3 +++ .eslintrc | 1 + 2 files changed, 4 insertions(+) create mode 100644 .babelrc diff --git a/.babelrc b/.babelrc new file mode 100644 index 000000000..84ed5f15b --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "plugins": ["@babel/plugin-proposal-class-properties"] +} diff --git a/.eslintrc b/.eslintrc index 899233215..85caae33d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,6 @@ { "extends": "standard", + "parser": "@babel/eslint-parser", "parserOptions": { "ecmaVersion": 2020 }, From 64a67eb98b5ccc2612ca13998559925f7e957612 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 05:58:51 +0200 Subject: [PATCH 08/64] Store data locally in the DeviceInterface Signed-off-by: Emanuele Feliziani --- src/DeviceInterface.js | 44 +++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index 10228cc75..5278fd9d9 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -38,6 +38,29 @@ const createAttachTooltip = (getAutofillData, refreshAlias, addresses) => (form, let attempts = 0 class InterfacePrototype { + // Data + #addresses = {} + get hasLocalAddresses () { + return this.#addresses.privateAddress && this.#addresses.personalAddress + } + getLocalAddresses () { + return this.#addresses + } + storeLocalAddresses (addresses) { + this.#addresses = addresses + } + + #credentials = [] + get hasLocalCredentials () { + return this.#credentials.length + } + getLocalCredentials () { + return this.#credentials.map(cred => delete cred.password && cred) + } + storeLocalCredentials (credentials) { + this.#credentials = credentials.map(cred => delete cred.password && cred) + } + init () { const start = () => { this.addDeviceListeners() @@ -91,12 +114,16 @@ class ExtensionInterface extends InterfacePrototype { this.getAddresses = () => new Promise(resolve => chrome.runtime.sendMessage( {getAddresses: true}, - (data) => resolve(data) + (data) => { + this.storeLocalAddresses(data) + return resolve(data) + } )) this.refreshAlias = () => chrome.runtime.sendMessage( {refreshAlias: true}, - (addresses) => { this.addresses = addresses }) + (addresses) => this.storeLocalAddresses(addresses) + ) this.trySigningIn = () => { if (isDDGDomain()) { @@ -192,6 +219,7 @@ class AppleDeviceInterface extends InterfacePrototype { const signedIn = await this.isDeviceSignedIn() if (signedIn) { this.attachTooltip = createAttachTooltip(this.getAddresses, this.refreshAlias, {}) + await this.getAddresses() notifyWebApp({ deviceSignedIn: {value: true, shouldLog} }) scanForInputs(this) } else { @@ -203,6 +231,7 @@ class AppleDeviceInterface extends InterfacePrototype { if (!isApp) return this.getAlias() const {addresses} = await wkSendAndWait('emailHandlerGetAddresses') + this.storeLocalAddresses(addresses) return addresses } @@ -253,11 +282,12 @@ class AppleDeviceInterface extends InterfacePrototype { * Gets a list of credentials for the current site * @returns {Promise<{ success: [CredentialsObject], error?: String }>} */ - this.getCredentials = () => wkSendAndWait('pmHandlerGetCredentials') - .then((response) => { - console.log(response) - return response - }) + this.getCredentials = () => + wkSendAndWait('pmHandlerGetCredentials') + .then((response) => { + this.storeLocalCredentials(response.success) + return response + }) /** * Gets credentials ready for autofill From 407252eafcdf28e82b5ab0149be063584b0d7d86 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 06:07:01 +0200 Subject: [PATCH 09/64] Improve dependency injection in createAttachTooltip Signed-off-by: Emanuele Feliziani --- src/DDGAutofill.js | 8 ++++---- src/DeviceInterface.js | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/DDGAutofill.js b/src/DDGAutofill.js index 6c7ad9f43..46e6827fa 100644 --- a/src/DDGAutofill.js +++ b/src/DDGAutofill.js @@ -7,12 +7,12 @@ const { } = require('./autofill-utils') class DDGAutofill { - constructor (input, associatedForm, getAddresses, refreshAlias, addresses) { + constructor (input, associatedForm, Interface) { const shadow = document.createElement('ddg-autofill').attachShadow({mode: 'closed'}) this.host = shadow.host this.input = input this.associatedForm = associatedForm - this.addresses = addresses + this.addresses = Interface.getLocalAddresses() this.animationFrame = null const includeStyles = isApp @@ -53,7 +53,7 @@ ${includeStyles} } // Get the alias from the extension - getAddresses().then(this.updateAddresses) + Interface.getAddresses().then(this.updateAddresses) this.top = 0 this.left = 0 @@ -157,7 +157,7 @@ ${includeStyles} safeExecute(this.usePersonalButton, () => { this.associatedForm.autofill(formatAddress(this.addresses.privateAddress)) - refreshAlias() + Interface.refreshAlias() }) }) } diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index 5278fd9d9..fc485cfd6 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -17,10 +17,10 @@ const scanForInputs = require('./scanForInputs.js') const SIGN_IN_MSG = { signMeIn: true } -const createAttachTooltip = (getAutofillData, refreshAlias, addresses) => (form, input) => { +const createAttachTooltip = (Interface) => (form, input) => { if (isDDGApp && !isApp) { form.activeInput = input - getAutofillData().then((alias) => { + Interface.getAlias().then((alias) => { if (alias) form.autofill(alias) else form.activeInput.focus() }) @@ -28,7 +28,7 @@ const createAttachTooltip = (getAutofillData, refreshAlias, addresses) => (form, if (form.tooltip) return form.activeInput = input - form.tooltip = new DDGAutofill(input, form, getAutofillData, refreshAlias, addresses) + form.tooltip = new DDGAutofill(input, form, Interface) form.intObs.observe(input) window.addEventListener('mousedown', form.removeTooltip, {capture: true}) window.addEventListener('input', form.removeTooltip, {once: true}) @@ -102,8 +102,7 @@ class ExtensionInterface extends InterfacePrototype { this.setupAutofill = ({shouldLog} = {shouldLog: false}) => { this.getAddresses().then(addresses => { - if (addresses?.privateAddress && addresses?.personalAddress) { - this.attachTooltip = createAttachTooltip(this.getAddresses, this.refreshAlias, addresses) + if (this.hasLocalAddresses) { notifyWebApp({ deviceSignedIn: {value: true, shouldLog} }) scanForInputs(this) } else { @@ -174,6 +173,8 @@ class ExtensionInterface extends InterfacePrototype { } }) } + + this.attachTooltip = createAttachTooltip(this) } } @@ -203,7 +204,7 @@ class AndroidInterface extends InterfacePrototype { this.storeUserData = ({addUserData: {token, userName}}) => window.EmailInterface.storeCredentials(token, userName) - this.attachTooltip = createAttachTooltip(this.getAlias) + this.attachTooltip = createAttachTooltip(this) } } @@ -218,7 +219,6 @@ class AppleDeviceInterface extends InterfacePrototype { this.setupAutofill = async ({shouldLog} = {shouldLog: false}) => { const signedIn = await this.isDeviceSignedIn() if (signedIn) { - this.attachTooltip = createAttachTooltip(this.getAddresses, this.refreshAlias, {}) await this.getAddresses() notifyWebApp({ deviceSignedIn: {value: true, shouldLog} }) scanForInputs(this) @@ -256,7 +256,7 @@ class AppleDeviceInterface extends InterfacePrototype { this.storeUserData = ({addUserData: {token, userName}}) => wkSend('emailHandlerStoreToken', { token, username: userName }) - this.attachTooltip = createAttachTooltip(this.getAlias, this.refreshAlias) + this.attachTooltip = createAttachTooltip(this) /** * PM endpoints From 3b222d18bd0fc2d87a61f0b56b02cc6ee228fd1b Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 06:08:53 +0200 Subject: [PATCH 10/64] Improve dependency injection in Form Signed-off-by: Emanuele Feliziani --- src/Form/Form.js | 5 +++-- src/scanForInputs.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Form/Form.js b/src/Form/Form.js index 57e9f8d6f..9315e35c3 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -21,10 +21,11 @@ const INLINE_AUTOFILLED_STYLES = { } class Form { - constructor (form, input, attachTooltip) { + constructor (form, input, DeviceInterface) { this.form = form this.formAnalyzer = new FormAnalyzer(form, input) - this.attachTooltip = attachTooltip + this.Device = DeviceInterface + this.attachTooltip = DeviceInterface.attachTooltip this.emailInputs = new Set() this.passwordInputs = new Set() this.touched = new Set() diff --git a/src/scanForInputs.js b/src/scanForInputs.js index ddea2c5c0..1fa1aa711 100644 --- a/src/scanForInputs.js +++ b/src/scanForInputs.js @@ -13,7 +13,7 @@ const scanForInputs = (DeviceInterface) => { // If we've already met the form, add the input forms.get(parentForm).addInput(input) } else { - forms.set(parentForm || input, new Form(parentForm, input, DeviceInterface.attachTooltip)) + forms.set(parentForm || input, new Form(parentForm, input, DeviceInterface)) } } From f120f6b54b3b9e76d74a261ed2eb9900be9463ae Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 06:09:35 +0200 Subject: [PATCH 11/64] Expand selector to password fields Signed-off-by: Emanuele Feliziani --- src/Form/selectors.js | 4 +++- src/scanForInputs.js | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Form/selectors.js b/src/Form/selectors.js index 66523f908..31260fe9e 100644 --- a/src/Form/selectors.js +++ b/src/Form/selectors.js @@ -16,6 +16,8 @@ const EMAIL_SELECTOR = ` const PASSWORD_SELECTOR = `input[type=password]:not([autocomplete*=cc]):not([autocomplete=one-time-code])` +const FIELD_SELECTOR = [PASSWORD_SELECTOR, EMAIL_SELECTOR].join(', ') + const SUBMIT_BUTTON_SELECTOR = 'input[type=submit], input[type=button], button[type=submit], [role=button]' -module.exports = {EMAIL_SELECTOR, PASSWORD_SELECTOR, SUBMIT_BUTTON_SELECTOR} +module.exports = {EMAIL_SELECTOR, PASSWORD_SELECTOR, FIELD_SELECTOR, SUBMIT_BUTTON_SELECTOR} diff --git a/src/scanForInputs.js b/src/scanForInputs.js index 1fa1aa711..96012ff86 100644 --- a/src/scanForInputs.js +++ b/src/scanForInputs.js @@ -1,6 +1,6 @@ const Form = require('./Form/Form') const {notifyWebApp} = require('./autofill-utils') -const {EMAIL_SELECTOR, PASSWORD_SELECTOR} = require('./Form/selectors') +const {FIELD_SELECTOR} = require('./Form/selectors') // Accepts the DeviceInterface as an explicit dependency const scanForInputs = (DeviceInterface) => { @@ -18,10 +18,10 @@ const scanForInputs = (DeviceInterface) => { } const findEligibleInput = context => { - if (context.nodeName === 'INPUT' && context.matches(EMAIL_SELECTOR)) { + if (context.nodeName === 'INPUT' && context.matches(FIELD_SELECTOR)) { addInput(context) } else { - context.querySelectorAll(EMAIL_SELECTOR).forEach(addInput) + context.querySelectorAll(FIELD_SELECTOR).forEach(addInput) } } From b72964fbd4a92451017af58ef1ea884617a7e609 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 06:10:46 +0200 Subject: [PATCH 12/64] Support re-triggering of input decoration Signed-off-by: Emanuele Feliziani --- src/DeviceInterface.js | 2 +- src/Form/Form.js | 4 ++++ src/scanForInputs.js | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index fc485cfd6..80f29cdb5 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -13,7 +13,7 @@ const { wkSend, wkSendAndWait } = require('./appleDeviceUtils/appleDeviceUtils') -const scanForInputs = require('./scanForInputs.js') +const {scanForInputs, forms} = require('./scanForInputs.js') const SIGN_IN_MSG = { signMeIn: true } diff --git a/src/Form/Form.js b/src/Form/Form.js index 9315e35c3..0ff8c5a5c 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -67,6 +67,10 @@ class Form { this.execOnInputs(this.removeInputDecoration) this.listeners.forEach(({el, type, fn}) => el.removeEventListener(type, fn)) } + this.redecorateAllInputs = () => { + this.removeAllDecorations() + this.execOnInputs(this.decorateInput) + } this.resetAllInputs = () => { this.execOnInputs((input) => { setValue(input, '') diff --git a/src/scanForInputs.js b/src/scanForInputs.js index 96012ff86..dbba5346a 100644 --- a/src/scanForInputs.js +++ b/src/scanForInputs.js @@ -2,10 +2,10 @@ const Form = require('./Form/Form') const {notifyWebApp} = require('./autofill-utils') const {FIELD_SELECTOR} = require('./Form/selectors') +const forms = new Map() + // Accepts the DeviceInterface as an explicit dependency const scanForInputs = (DeviceInterface) => { - const forms = new Map() - const addInput = input => { const parentForm = input.form @@ -62,4 +62,4 @@ const scanForInputs = (DeviceInterface) => { }) } -module.exports = scanForInputs +module.exports = {scanForInputs, forms} From d5a799c974bab231700f40653a8a54bb0638612b Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 06:12:24 +0200 Subject: [PATCH 13/64] Support PM flow Signed-off-by: Emanuele Feliziani --- src/DeviceInterface.js | 8 +++++++- src/Form/Form.js | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index 80f29cdb5..c36b03fd4 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -217,14 +217,20 @@ class AppleDeviceInterface extends InterfacePrototype { } this.setupAutofill = async ({shouldLog} = {shouldLog: false}) => { + if (isApp) { + await this.getCredentials() + } + const signedIn = await this.isDeviceSignedIn() if (signedIn) { await this.getAddresses() notifyWebApp({ deviceSignedIn: {value: true, shouldLog} }) - scanForInputs(this) + forms.forEach(form => form.redecorateAllInputs) } else { this.trySigningIn() } + + scanForInputs(this) } this.getAddresses = async () => { diff --git a/src/Form/Form.js b/src/Form/Form.js index 0ff8c5a5c..692433de9 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -92,9 +92,11 @@ class Form { addInput (input) { if (input.type === 'password') { this.passwordInputs.add(input) + if (this.formAnalyzer.isLogin && this.Device.hasLocalCredentials) this.decorateInput(input) } else { this.emailInputs.add(input) if (this.formAnalyzer.isSignup) this.decorateInput(input) + if (this.formAnalyzer.isSignup && this.Device.hasLocalAddresses) this.decorateInput(input) } return this From 10ffaaddadccc2548cb85c29a6daa3035007d514 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 06:12:55 +0200 Subject: [PATCH 14/64] Add PM endpoint definition to interface base Signed-off-by: Emanuele Feliziani --- src/DeviceInterface.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index c36b03fd4..f566a906a 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -94,6 +94,11 @@ class InterfacePrototype { attachTooltip () {} isDeviceSignedIn () {} getAlias () {} + // PM endpoints + storeCredentials () {} + getCredentials () {} + getAutofillCredentials () {} + openManagePasswords () {} } class ExtensionInterface extends InterfacePrototype { From 1d82cdd6a2ff4ecb47141923290d5e7b36820c1e Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 06:40:59 +0200 Subject: [PATCH 15/64] Move UI files to separate folder Signed-off-by: Emanuele Feliziani --- Gruntfile.js | 6 +++--- src/DeviceInterface.js | 2 +- src/{ => UI}/DDGAutofill.js | 2 +- src/{ => UI}/styles/DDGAutofill-styles.js | 0 src/{ => UI}/styles/autofill-host-styles.css | 0 5 files changed, 5 insertions(+), 5 deletions(-) rename src/{ => UI}/DDGAutofill.js (99%) rename src/{ => UI}/styles/DDGAutofill-styles.js (100%) rename src/{ => UI}/styles/autofill-host-styles.css (100%) diff --git a/Gruntfile.js b/Gruntfile.js index 7ea3c1384..5fe614821 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -27,10 +27,10 @@ module.exports = function (grunt) { target: 'src/**/*.js' }, exec: { - copyAutofillStylesToCSS: 'cp src/styles/DDGAutofill-styles.js dist/autofill.css && sed -i "" \'/`/d\' dist/autofill.css', - copyHostStyles: 'cp src/styles/autofill-host-styles.css dist/autofill-host-styles_chrome.css && cp src/styles/autofill-host-styles.css dist/autofill-host-styles_firefox.css', + copyAutofillStylesToCSS: 'cp src/UI/styles/DDGAutofill-styles.js dist/autofill.css && sed -i "" \'/`/d\' dist/autofill.css', + copyHostStyles: 'cp src/UI/styles/autofill-host-styles.css dist/autofill-host-styles_chrome.css && cp src/UI/styles/autofill-host-styles.css dist/autofill-host-styles_firefox.css', // Firefox and Chrome treat relative url differently in injected scripts. This fixes it. - updateFirefoxRelativeUrl: `sed -i "" "s/chrome-extension:\\/\\/__MSG_@@extension_id__\\/public/../g" dist/autofill-host-styles_firefox.css`, + updateFirefoxRelativeUrl: `sed -i "" "s/chrome-extension:\\/\\/__MSG_@@extension_id__\\/public/../g" dist/autofill-host-styles_firefox.css` }, /** * Run predefined tasks whenever watched files are added, diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index f566a906a..59a780df2 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -1,4 +1,4 @@ -const DDGAutofill = require('./DDGAutofill') +const DDGAutofill = require('./UI/DDGAutofill') const { isApp, notifyWebApp, diff --git a/src/DDGAutofill.js b/src/UI/DDGAutofill.js similarity index 99% rename from src/DDGAutofill.js rename to src/UI/DDGAutofill.js index 46e6827fa..800655f44 100644 --- a/src/DDGAutofill.js +++ b/src/UI/DDGAutofill.js @@ -4,7 +4,7 @@ const { getDaxBoundingBox, safeExecute, escapeXML -} = require('./autofill-utils') +} = require('../autofill-utils') class DDGAutofill { constructor (input, associatedForm, Interface) { diff --git a/src/styles/DDGAutofill-styles.js b/src/UI/styles/DDGAutofill-styles.js similarity index 100% rename from src/styles/DDGAutofill-styles.js rename to src/UI/styles/DDGAutofill-styles.js diff --git a/src/styles/autofill-host-styles.css b/src/UI/styles/autofill-host-styles.css similarity index 100% rename from src/styles/autofill-host-styles.css rename to src/UI/styles/autofill-host-styles.css From 61a51faac13e45a5ce5dd5a9d5d9e6be64d93792 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 14:12:14 +0200 Subject: [PATCH 16/64] Abstract a generic Tooltip class Signed-off-by: Emanuele Feliziani --- src/UI/DDGAutofill.js | 129 +++++++----------------------------------- src/UI/Tooltip.js | 110 +++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 108 deletions(-) create mode 100644 src/UI/Tooltip.js diff --git a/src/UI/DDGAutofill.js b/src/UI/DDGAutofill.js index 800655f44..13890826b 100644 --- a/src/UI/DDGAutofill.js +++ b/src/UI/DDGAutofill.js @@ -1,25 +1,25 @@ const { isApp, formatAddress, - getDaxBoundingBox, safeExecute, escapeXML } = require('../autofill-utils') +const Tooltip = require('./Tooltip') -class DDGAutofill { +class EmailAutofill extends Tooltip { constructor (input, associatedForm, Interface) { - const shadow = document.createElement('ddg-autofill').attachShadow({mode: 'closed'}) - this.host = shadow.host + super() + this.shadow = document.createElement('ddg-autofill').attachShadow({mode: 'closed'}) + this.host = this.shadow.host this.input = input this.associatedForm = associatedForm this.addresses = Interface.getLocalAddresses() - this.animationFrame = null const includeStyles = isApp ? `` : `` - shadow.innerHTML = ` + this.shadow.innerHTML = ` ${includeStyles}
` - this.wrapper = shadow.querySelector('.wrapper') - this.tooltip = shadow.querySelector('.tooltip') - this.usePersonalButton = shadow.querySelector('.js-use-personal') - this.usePrivateButton = shadow.querySelector('.js-use-private') - this.addressEl = shadow.querySelector('.js-address') - this.stylesheet = shadow.querySelector('link, style') - // Un-hide once the style is loaded, to avoid flashing unstyled content - this.stylesheet.addEventListener('load', () => - this.tooltip.removeAttribute('hidden')) + this.wrapper = this.shadow.querySelector('.wrapper') + this.tooltip = this.shadow.querySelector('.tooltip') + this.usePersonalButton = this.shadow.querySelector('.js-use-personal') + this.usePrivateButton = this.shadow.querySelector('.js-use-private') + this.addressEl = this.shadow.querySelector('.js-address') this.updateAddresses = (addresses) => { if (addresses) { @@ -51,98 +47,6 @@ ${includeStyles} this.addressEl.textContent = formatAddress(addresses.personalAddress) } } - - // Get the alias from the extension - Interface.getAddresses().then(this.updateAddresses) - - this.top = 0 - this.left = 0 - this.transformRuleIndex = null - this.updatePosition = ({left, top}) => { - // If the stylesheet is not loaded wait for load (Chrome bug) - if (!shadow.styleSheets.length) return this.stylesheet.addEventListener('load', this.checkPosition) - - this.left = left - this.top = top - - if (this.transformRuleIndex && shadow.styleSheets[this.transformRuleIndex]) { - // If we have already set the rule, remove it… - shadow.styleSheets[0].deleteRule(this.transformRuleIndex) - } else { - // …otherwise, set the index as the very last rule - this.transformRuleIndex = shadow.styleSheets[0].rules.length - } - - const newRule = `.wrapper {transform: translate(${left}px, ${top}px);}` - shadow.styleSheets[0].insertRule(newRule, this.transformRuleIndex) - } - - this.append = () => document.body.appendChild(shadow.host) - this.append() - this.lift = () => { - this.left = null - this.top = null - document.body.removeChild(this.host) - } - - this.remove = () => { - window.removeEventListener('scroll', this.checkPosition, {passive: true, capture: true}) - this.resObs.disconnect() - this.mutObs.disconnect() - this.lift() - } - - this.checkPosition = () => { - if (this.animationFrame) { - window.cancelAnimationFrame(this.animationFrame) - } - - this.animationFrame = window.requestAnimationFrame(() => { - const {left, bottom} = getDaxBoundingBox(this.input) - - if (left !== this.left || bottom !== this.top) { - this.updatePosition({left, top: bottom}) - } - - this.animationFrame = null - }) - } - this.resObs = new ResizeObserver(entries => entries.forEach(this.checkPosition)) - this.resObs.observe(document.body) - this.count = 0 - this.ensureIsLastInDOM = () => { - // If DDG el is not the last in the doc, move it there - if (document.body.lastElementChild !== this.host) { - this.lift() - - // Try up to 5 times to avoid infinite loop in case someone is doing the same - if (this.count < 15) { - this.append() - this.checkPosition() - this.count++ - } else { - // Reset count so we can resume normal flow - this.count = 0 - console.info(`DDG autofill bailing out`) - } - } - } - this.mutObs = new MutationObserver((mutationList) => { - for (const mutationRecord of mutationList) { - if (mutationRecord.type === 'childList') { - // Only check added nodes - mutationRecord.addedNodes.forEach(el => { - if (el.nodeName === 'DDG-AUTOFILL') return - - this.ensureIsLastInDOM() - }) - } - } - this.checkPosition() - }) - this.mutObs.observe(document.body, {childList: true, subtree: true, attributes: true}) - window.addEventListener('scroll', this.checkPosition, {passive: true, capture: true}) - this.usePersonalButton.addEventListener('click', (e) => { if (!e.isTrusted) return e.stopImmediatePropagation() @@ -160,7 +64,16 @@ ${includeStyles} Interface.refreshAlias() }) }) + + // Get the alias from the extension + Interface.getAddresses().then(this.updateAddresses) + + this.init() + this.append() + this.resObs.observe(document.body) + this.mutObs.observe(document.body, {childList: true, subtree: true, attributes: true}) + window.addEventListener('scroll', this.checkPosition, {passive: true, capture: true}) } } -module.exports = DDGAutofill +module.exports = EmailAutofill diff --git a/src/UI/Tooltip.js b/src/UI/Tooltip.js new file mode 100644 index 000000000..630e3ef62 --- /dev/null +++ b/src/UI/Tooltip.js @@ -0,0 +1,110 @@ +const {getDaxBoundingBox} = require('../autofill-utils') + +const updatePosition = function ({left, top}) { + const shadow = this.shadow + // If the stylesheet is not loaded wait for load (Chrome bug) + if (!shadow.styleSheets.length) return this.stylesheet.addEventListener('load', this.checkPosition) + + this.left = left + this.top = top + + if (this.transformRuleIndex && shadow.styleSheets[this.transformRuleIndex]) { + // If we have already set the rule, remove it… + shadow.styleSheets[0].deleteRule(this.transformRuleIndex) + } else { + // …otherwise, set the index as the very last rule + this.transformRuleIndex = shadow.styleSheets[0].rules.length + } + + const newRule = `.wrapper {transform: translate(${left}px, ${top}px);}` + shadow.styleSheets[0].insertRule(newRule, this.transformRuleIndex) +} + +const checkPosition = function () { + if (this.animationFrame) { + window.cancelAnimationFrame(this.animationFrame) + } + + this.animationFrame = window.requestAnimationFrame(() => { + const {left, bottom} = getDaxBoundingBox(this.input) + + if (left !== this.left || bottom !== this.top) { + this.updatePosition({left, top: bottom}) + } + + this.animationFrame = null + }) +} + +const ensureIsLastInDOM = function () { + // If DDG el is not the last in the doc, move it there + if (document.body.lastElementChild !== this.host) { + this.lift() + + // Try up to 5 times to avoid infinite loop in case someone is doing the same + if (this.count < 15) { + this.append() + this.checkPosition() + this.count++ + } else { + // Reset count so we can resume normal flow + this.count = 0 + console.info(`DDG autofill bailing out`) + } + } +} + +class Tooltip { + append () { + document.body.appendChild(this.host) + } + remove () { + window.removeEventListener('scroll', this.checkPosition, {passive: true, capture: true}) + this.resObs.disconnect() + this.mutObs.disconnect() + this.lift() + } + lift () { + this.left = null + this.top = null + document.body.removeChild(this.host) + } + checkPosition = checkPosition.bind(this) + updatePosition = updatePosition.bind(this) + ensureIsLastInDOM = ensureIsLastInDOM.bind(this) + mutObs = new MutationObserver((mutationList) => { + for (const mutationRecord of mutationList) { + if (mutationRecord.type === 'childList') { + // Only check added nodes + mutationRecord.addedNodes.forEach(el => { + if (el.nodeName === 'DDG-AUTOFILL') return + + this.ensureIsLastInDOM() + }) + } + } + this.checkPosition() + }) + init () { + this.animationFrame = null + this.top = 0 + this.left = 0 + this.transformRuleIndex = null + + this.resObs = new ResizeObserver(entries => entries.forEach(this.checkPosition)) + this.count = 0 + + this.stylesheet = this.shadow.querySelector('link, style') + // Un-hide once the style is loaded, to avoid flashing unstyled content + this.stylesheet.addEventListener('load', () => + this.tooltip.removeAttribute('hidden')) + } + setupStylesheet () { + this.stylesheet = this.shadow.querySelector('link, style') + // Un-hide once the style is loaded, to avoid flashing unstyled content + this.stylesheet.addEventListener('load', () => + this.tooltip.removeAttribute('hidden')) + } +} + +module.exports = Tooltip From ae6842e125773e1df885689e0bb5ccfa8c73cd08 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 14:13:24 +0200 Subject: [PATCH 17/64] Rename EmailAutofill class Signed-off-by: Emanuele Feliziani --- src/DeviceInterface.js | 4 ++-- src/UI/{DDGAutofill.js => EmailAutofill.js} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/UI/{DDGAutofill.js => EmailAutofill.js} (100%) diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index 59a780df2..9428cdfa3 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -1,4 +1,4 @@ -const DDGAutofill = require('./UI/DDGAutofill') +const EmailAutofill = require('./UI/EmailAutofill') const { isApp, notifyWebApp, @@ -28,7 +28,7 @@ const createAttachTooltip = (Interface) => (form, input) => { if (form.tooltip) return form.activeInput = input - form.tooltip = new DDGAutofill(input, form, Interface) + form.tooltip = new EmailAutofill(input, form, Interface) form.intObs.observe(input) window.addEventListener('mousedown', form.removeTooltip, {capture: true}) window.addEventListener('input', form.removeTooltip, {once: true}) diff --git a/src/UI/DDGAutofill.js b/src/UI/EmailAutofill.js similarity index 100% rename from src/UI/DDGAutofill.js rename to src/UI/EmailAutofill.js From 78d46b4bb5ce9c07388da7542fe4922d23802377 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 15:52:53 +0200 Subject: [PATCH 18/64] Improve tooltip removal Signed-off-by: Emanuele Feliziani --- src/UI/Tooltip.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/UI/Tooltip.js b/src/UI/Tooltip.js index 630e3ef62..a33574ced 100644 --- a/src/UI/Tooltip.js +++ b/src/UI/Tooltip.js @@ -37,18 +37,18 @@ const checkPosition = function () { } const ensureIsLastInDOM = function () { + this.count = this.count || 0 // If DDG el is not the last in the doc, move it there if (document.body.lastElementChild !== this.host) { - this.lift() - // Try up to 5 times to avoid infinite loop in case someone is doing the same if (this.count < 15) { + this.lift() this.append() this.checkPosition() this.count++ } else { - // Reset count so we can resume normal flow - this.count = 0 + // Remove the tooltip from the form to cleanup listeners and observers + this.associatedForm.removeTooltip() console.info(`DDG autofill bailing out`) } } @@ -72,6 +72,7 @@ class Tooltip { checkPosition = checkPosition.bind(this) updatePosition = updatePosition.bind(this) ensureIsLastInDOM = ensureIsLastInDOM.bind(this) + resObs = new ResizeObserver(entries => entries.forEach(this.checkPosition)) mutObs = new MutationObserver((mutationList) => { for (const mutationRecord of mutationList) { if (mutationRecord.type === 'childList') { @@ -91,9 +92,6 @@ class Tooltip { this.left = 0 this.transformRuleIndex = null - this.resObs = new ResizeObserver(entries => entries.forEach(this.checkPosition)) - this.count = 0 - this.stylesheet = this.shadow.querySelector('link, style') // Un-hide once the style is loaded, to avoid flashing unstyled content this.stylesheet.addEventListener('load', () => From 6741603177c3dcb1fa918cb007d471b995d7ca0c Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 16:06:27 +0200 Subject: [PATCH 19/64] Move more logic into the Tooltip parent class Signed-off-by: Emanuele Feliziani --- src/UI/EmailAutofill.js | 17 +++++------------ src/UI/Tooltip.js | 12 ++++++++++++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/UI/EmailAutofill.js b/src/UI/EmailAutofill.js index 13890826b..39566e68d 100644 --- a/src/UI/EmailAutofill.js +++ b/src/UI/EmailAutofill.js @@ -8,12 +8,9 @@ const Tooltip = require('./Tooltip') class EmailAutofill extends Tooltip { constructor (input, associatedForm, Interface) { - super() - this.shadow = document.createElement('ddg-autofill').attachShadow({mode: 'closed'}) - this.host = this.shadow.host - this.input = input - this.associatedForm = associatedForm - this.addresses = Interface.getLocalAddresses() + super(input, associatedForm, Interface) + + this.addresses = this.interface.getLocalAddresses() const includeStyles = isApp ? `` @@ -61,18 +58,14 @@ ${includeStyles} safeExecute(this.usePersonalButton, () => { this.associatedForm.autofill(formatAddress(this.addresses.privateAddress)) - Interface.refreshAlias() + this.interface.refreshAlias() }) }) // Get the alias from the extension - Interface.getAddresses().then(this.updateAddresses) + this.interface.getAddresses().then(this.updateAddresses) this.init() - this.append() - this.resObs.observe(document.body) - this.mutObs.observe(document.body, {childList: true, subtree: true, attributes: true}) - window.addEventListener('scroll', this.checkPosition, {passive: true, capture: true}) } } diff --git a/src/UI/Tooltip.js b/src/UI/Tooltip.js index a33574ced..ee9b6cbeb 100644 --- a/src/UI/Tooltip.js +++ b/src/UI/Tooltip.js @@ -55,6 +55,13 @@ const ensureIsLastInDOM = function () { } class Tooltip { + constructor (input, associatedForm, Interface) { + this.shadow = document.createElement('ddg-autofill').attachShadow({mode: 'closed'}) + this.host = this.shadow.host + this.input = input + this.associatedForm = associatedForm + this.interface = Interface + } append () { document.body.appendChild(this.host) } @@ -96,6 +103,11 @@ class Tooltip { // Un-hide once the style is loaded, to avoid flashing unstyled content this.stylesheet.addEventListener('load', () => this.tooltip.removeAttribute('hidden')) + + this.append() + this.resObs.observe(document.body) + this.mutObs.observe(document.body, {childList: true, subtree: true, attributes: true}) + window.addEventListener('scroll', this.checkPosition, {passive: true, capture: true}) } setupStylesheet () { this.stylesheet = this.shadow.querySelector('link, style') From b36399293af845ad1536b25fc82e0cf88ba94046 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 16:08:55 +0200 Subject: [PATCH 20/64] Simplify attachTooltip Signed-off-by: Emanuele Feliziani --- src/DeviceInterface.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index 9428cdfa3..949fe8f6a 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -1,4 +1,6 @@ const EmailAutofill = require('./UI/EmailAutofill') +const CredentialsAutofill = require('./UI/CredentialsAutofill') +const {PASSWORD_SELECTOR} = require('./Form/selectors') const { isApp, notifyWebApp, @@ -17,10 +19,10 @@ const {scanForInputs, forms} = require('./scanForInputs.js') const SIGN_IN_MSG = { signMeIn: true } -const createAttachTooltip = (Interface) => (form, input) => { +const attachTooltip = function (form, input) { if (isDDGApp && !isApp) { form.activeInput = input - Interface.getAlias().then((alias) => { + this.getAlias().then((alias) => { if (alias) form.autofill(alias) else form.activeInput.focus() }) @@ -28,7 +30,9 @@ const createAttachTooltip = (Interface) => (form, input) => { if (form.tooltip) return form.activeInput = input - form.tooltip = new EmailAutofill(input, form, Interface) + form.tooltip = input.matches(PASSWORD_SELECTOR) + ? new CredentialsAutofill(input, form, this) + : new EmailAutofill(input, form, this) form.intObs.observe(input) window.addEventListener('mousedown', form.removeTooltip, {capture: true}) window.addEventListener('input', form.removeTooltip, {once: true}) @@ -62,6 +66,7 @@ class InterfacePrototype { } init () { + this.attachTooltip = attachTooltip.bind(this) const start = () => { this.addDeviceListeners() this.setupAutofill() @@ -178,8 +183,6 @@ class ExtensionInterface extends InterfacePrototype { } }) } - - this.attachTooltip = createAttachTooltip(this) } } @@ -208,8 +211,6 @@ class AndroidInterface extends InterfacePrototype { this.storeUserData = ({addUserData: {token, userName}}) => window.EmailInterface.storeCredentials(token, userName) - - this.attachTooltip = createAttachTooltip(this) } } @@ -267,8 +268,6 @@ class AppleDeviceInterface extends InterfacePrototype { this.storeUserData = ({addUserData: {token, userName}}) => wkSend('emailHandlerStoreToken', { token, username: userName }) - this.attachTooltip = createAttachTooltip(this) - /** * PM endpoints */ From 3572aa740027e61e1ac15c9afa5a66461060d2c0 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Fri, 11 Jun 2021 18:45:27 +0200 Subject: [PATCH 21/64] Add basic password autofill Signed-off-by: Emanuele Feliziani --- src/DeviceInterface.js | 2 +- src/Form/Form.js | 31 +++++++++++++++++----- src/UI/CredentialsAutofill.js | 49 +++++++++++++++++++++++++++++++++++ src/UI/EmailAutofill.js | 4 +-- 4 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 src/UI/CredentialsAutofill.js diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index 949fe8f6a..05c81e3a9 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -23,7 +23,7 @@ const attachTooltip = function (form, input) { if (isDDGApp && !isApp) { form.activeInput = input this.getAlias().then((alias) => { - if (alias) form.autofill(alias) + if (alias) form.autofillEmail(alias) else form.activeInput.focus() }) } else { diff --git a/src/Form/Form.js b/src/Form/Form.js index 692433de9..6a4c7d236 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -1,4 +1,5 @@ const FormAnalyzer = require('./FormAnalyzer') +const {PASSWORD_SELECTOR} = require("./selectors"); const {addInlineStyles, removeInlineStyles, isDDGApp, isApp, setValue, isEventWithinDax} = require('../autofill-utils') const {daxBase64} = require('./logo-svg') @@ -87,6 +88,7 @@ class Form { execOnInputs (fn) { this.emailInputs.forEach(fn) + this.passwordInputs.forEach(fn) } addInput (input) { @@ -146,14 +148,29 @@ class Form { return (!this.touched.has(input) && this.areAllInputsEmpty()) || isEventWithinDax(e, input) } - autofill (alias) { - this.execOnInputs((input) => { - setValue(input, alias) - input.classList.add('ddg-autofilled') - addInlineStyles(input, INLINE_AUTOFILLED_STYLES) + autofillInput = (input, string) => { + setValue(input, string) + input.classList.add('ddg-autofilled') + addInlineStyles(input, INLINE_AUTOFILLED_STYLES) + + // If the user changes the alias, remove the decoration + input.addEventListener('input', this.removeAllHighlights, {once: true}) + } - // If the user changes the alias, remove the decoration - input.addEventListener('input', this.removeAllHighlights, {once: true}) + autofillEmail (alias) { + this.execOnInputs((input) => !input.matches(PASSWORD_SELECTOR) && this.autofillInput(input, alias)) + if (this.tooltip) { + this.removeTooltip() + } + } + + autofillCredentials (credentials) { + this.execOnInputs((input) => { + if (input.matches(PASSWORD_SELECTOR)) { + this.autofillInput(input, credentials.password) + } else { + this.autofillInput(input, credentials.username) + } }) if (this.tooltip) { this.removeTooltip() diff --git a/src/UI/CredentialsAutofill.js b/src/UI/CredentialsAutofill.js new file mode 100644 index 000000000..938c5df11 --- /dev/null +++ b/src/UI/CredentialsAutofill.js @@ -0,0 +1,49 @@ +const { + isApp, + formatAddress, + safeExecute, + escapeXML +} = require('../autofill-utils') +const Tooltip = require('./Tooltip') + +class CredentialsAutofill extends Tooltip { + constructor (input, associatedForm, Interface) { + super(input, associatedForm, Interface) + + this.credentials = this.interface.getLocalCredentials() + + const includeStyles = isApp + ? `` + : `` + + this.shadow.innerHTML = ` +${includeStyles} +
+ +
` + this.wrapper = this.shadow.querySelector('.wrapper') + this.tooltip = this.shadow.querySelector('.tooltip') + this.autofillButton = this.shadow.querySelector('.js-autofill-button') + + this.autofillButton.addEventListener('click', (e) => { + if (!e.isTrusted) return + e.stopImmediatePropagation() + + safeExecute(this.autofillButton, () => { + this.interface.getAutofillCredentials().then(({success, error}) => { + if (success) this.associatedForm.autofillCredentials(success) + }) + }) + }) + + this.init() + } +} + +module.exports = CredentialsAutofill diff --git a/src/UI/EmailAutofill.js b/src/UI/EmailAutofill.js index 39566e68d..cbb6a9a19 100644 --- a/src/UI/EmailAutofill.js +++ b/src/UI/EmailAutofill.js @@ -49,7 +49,7 @@ ${includeStyles} e.stopImmediatePropagation() safeExecute(this.usePersonalButton, () => { - this.associatedForm.autofill(formatAddress(this.addresses.personalAddress)) + this.associatedForm.autofillEmail(formatAddress(this.addresses.personalAddress)) }) }) this.usePrivateButton.addEventListener('click', (e) => { @@ -57,7 +57,7 @@ ${includeStyles} e.stopImmediatePropagation() safeExecute(this.usePersonalButton, () => { - this.associatedForm.autofill(formatAddress(this.addresses.privateAddress)) + this.associatedForm.autofillEmail(formatAddress(this.addresses.privateAddress)) this.interface.refreshAlias() }) }) From a002547114a0d05bb4a3d4b78788789cab3b6f49 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Mon, 14 Jun 2021 13:43:11 +0200 Subject: [PATCH 22/64] Fix lint errors and commit compiled file Signed-off-by: Emanuele Feliziani --- dist/autofill.js | 712 +++++++++++++++++++++++----------- src/Form/Form.js | 3 +- src/UI/CredentialsAutofill.js | 1 - 3 files changed, 480 insertions(+), 236 deletions(-) diff --git a/dist/autofill.js b/dist/autofill.js index fc251685a..70e438f73 100644 --- a/dist/autofill.js +++ b/dist/autofill.js @@ -1,179 +1,23 @@ (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i".concat(require('./styles/DDGAutofill-styles.js'), "") : ""); - shadow.innerHTML = "\n".concat(includeStyles, "\n
\n \n
"); - this.wrapper = shadow.querySelector('.wrapper'); - this.tooltip = shadow.querySelector('.tooltip'); - this.usePersonalButton = shadow.querySelector('.js-use-personal'); - this.usePrivateButton = shadow.querySelector('.js-use-private'); - this.addressEl = shadow.querySelector('.js-address'); - this.stylesheet = shadow.querySelector('link, style'); // Un-hide once the style is loaded, to avoid flashing unstyled content - - this.stylesheet.addEventListener('load', () => this.tooltip.removeAttribute('hidden')); - - this.updateAddresses = addresses => { - if (addresses) { - this.addresses = addresses; - this.addressEl.textContent = formatAddress(addresses.personalAddress); - } - }; // Get the alias from the extension - - - getAddresses().then(this.updateAddresses); - this.top = 0; - this.left = 0; - this.transformRuleIndex = null; - - this.updatePosition = ({ - left, - top - }) => { - // If the stylesheet is not loaded wait for load (Chrome bug) - if (!shadow.styleSheets.length) return this.stylesheet.addEventListener('load', this.checkPosition); - this.left = left; - this.top = top; - - if (this.transformRuleIndex && shadow.styleSheets[this.transformRuleIndex]) { - // If we have already set the rule, remove it… - shadow.styleSheets[0].deleteRule(this.transformRuleIndex); - } else { - // …otherwise, set the index as the very last rule - this.transformRuleIndex = shadow.styleSheets[0].rules.length; - } - - const newRule = ".wrapper {transform: translate(".concat(left, "px, ").concat(top, "px);}"); - shadow.styleSheets[0].insertRule(newRule, this.transformRuleIndex); - }; - - this.append = () => document.body.appendChild(shadow.host); - - this.append(); - - this.lift = () => { - this.left = null; - this.top = null; - document.body.removeChild(this.host); - }; - - this.remove = () => { - window.removeEventListener('scroll', this.checkPosition, { - passive: true, - capture: true - }); - this.resObs.disconnect(); - this.mutObs.disconnect(); - this.lift(); - }; - - this.checkPosition = () => { - if (this.animationFrame) { - window.cancelAnimationFrame(this.animationFrame); - } - - this.animationFrame = window.requestAnimationFrame(() => { - const { - left, - bottom - } = getDaxBoundingBox(this.input); +function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; } - if (left !== this.left || bottom !== this.top) { - this.updatePosition({ - left, - top: bottom - }); - } +function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } } - this.animationFrame = null; - }); - }; +function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); } - this.resObs = new ResizeObserver(entries => entries.forEach(this.checkPosition)); - this.resObs.observe(document.body); - this.count = 0; +function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); } - this.ensureIsLastInDOM = () => { - // If DDG el is not the last in the doc, move it there - if (document.body.lastElementChild !== this.host) { - this.lift(); // Try up to 5 times to avoid infinite loop in case someone is doing the same +function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; } - if (this.count < 15) { - this.append(); - this.checkPosition(); - this.count++; - } else { - // Reset count so we can resume normal flow - this.count = 0; - console.info("DDG autofill bailing out"); - } - } - }; +const EmailAutofill = require('./UI/EmailAutofill'); - this.mutObs = new MutationObserver(mutationList => { - for (const mutationRecord of mutationList) { - if (mutationRecord.type === 'childList') { - // Only check added nodes - mutationRecord.addedNodes.forEach(el => { - if (el.nodeName === 'DDG-AUTOFILL') return; - this.ensureIsLastInDOM(); - }); - } - } - - this.checkPosition(); - }); - this.mutObs.observe(document.body, { - childList: true, - subtree: true, - attributes: true - }); - window.addEventListener('scroll', this.checkPosition, { - passive: true, - capture: true - }); - this.usePersonalButton.addEventListener('click', e => { - if (!e.isTrusted) return; - e.stopImmediatePropagation(); - safeExecute(this.usePersonalButton, () => { - this.associatedForm.autofill(formatAddress(this.addresses.personalAddress)); - }); - }); - this.usePrivateButton.addEventListener('click', e => { - if (!e.isTrusted) return; - e.stopImmediatePropagation(); - safeExecute(this.usePersonalButton, () => { - this.associatedForm.autofill(formatAddress(this.addresses.privateAddress)); - refreshAlias(); - }); - }); - } +const CredentialsAutofill = require('./UI/CredentialsAutofill'); -} - -module.exports = DDGAutofill; - -},{"./autofill-utils":7,"./styles/DDGAutofill-styles.js":12}],2:[function(require,module,exports){ -"use strict"; - -const DDGAutofill = require('./DDGAutofill'); +const { + PASSWORD_SELECTOR +} = require('./Form/selectors'); const { isApp, @@ -191,22 +35,25 @@ const { wkSendAndWait } = require('./appleDeviceUtils/appleDeviceUtils'); -const scanForInputs = require('./scanForInputs.js'); +const { + scanForInputs, + forms +} = require('./scanForInputs.js'); const SIGN_IN_MSG = { signMeIn: true }; -const createAttachTooltip = (getAutofillData, refreshAlias, addresses) => (form, input) => { +const attachTooltip = function (form, input) { if (isDDGApp && !isApp) { form.activeInput = input; - getAutofillData().then(alias => { - if (alias) form.autofill(alias);else form.activeInput.focus(); + this.getAlias().then(alias => { + if (alias) form.autofillEmail(alias);else form.activeInput.focus(); }); } else { if (form.tooltip) return; form.activeInput = input; - form.tooltip = new DDGAutofill(input, form, getAutofillData, refreshAlias, addresses); + form.tooltip = input.matches(PASSWORD_SELECTOR) ? new CredentialsAutofill(input, form, this) : new EmailAutofill(input, form, this); form.intObs.observe(input); window.addEventListener('mousedown', form.removeTooltip, { capture: true @@ -219,8 +66,50 @@ const createAttachTooltip = (getAutofillData, refreshAlias, addresses) => (form, let attempts = 0; +var _addresses = new WeakMap(); + +var _credentials = new WeakMap(); + class InterfacePrototype { + constructor() { + _addresses.set(this, { + writable: true, + value: {} + }); + + _credentials.set(this, { + writable: true, + value: [] + }); + } + + get hasLocalAddresses() { + return _classPrivateFieldGet(this, _addresses).privateAddress && _classPrivateFieldGet(this, _addresses).personalAddress; + } + + getLocalAddresses() { + return _classPrivateFieldGet(this, _addresses); + } + + storeLocalAddresses(addresses) { + _classPrivateFieldSet(this, _addresses, addresses); + } + + get hasLocalCredentials() { + return _classPrivateFieldGet(this, _credentials).length; + } + + getLocalCredentials() { + return _classPrivateFieldGet(this, _credentials).map(cred => delete cred.password && cred); + } + + storeLocalCredentials(credentials) { + _classPrivateFieldSet(this, _credentials, credentials.map(cred => delete cred.password && cred)); + } + init() { + this.attachTooltip = attachTooltip.bind(this); + const start = () => { this.addDeviceListeners(); this.setupAutofill(); @@ -265,7 +154,16 @@ class InterfacePrototype { isDeviceSignedIn() {} - getAlias() {} + getAlias() {} // PM endpoints + + + storeCredentials() {} + + getCredentials() {} + + getAutofillCredentials() {} + + openManagePasswords() {} } @@ -279,8 +177,7 @@ class ExtensionInterface extends InterfacePrototype { shouldLog: false }) => { this.getAddresses().then(addresses => { - if (addresses !== null && addresses !== void 0 && addresses.privateAddress && addresses !== null && addresses !== void 0 && addresses.personalAddress) { - this.attachTooltip = createAttachTooltip(this.getAddresses, this.refreshAlias, addresses); + if (this.hasLocalAddresses) { notifyWebApp({ deviceSignedIn: { value: true, @@ -296,13 +193,14 @@ class ExtensionInterface extends InterfacePrototype { this.getAddresses = () => new Promise(resolve => chrome.runtime.sendMessage({ getAddresses: true - }, data => resolve(data))); + }, data => { + this.storeLocalAddresses(data); + return resolve(data); + })); this.refreshAlias = () => chrome.runtime.sendMessage({ refreshAlias: true - }, addresses => { - this.addresses = addresses; - }); + }, addresses => this.storeLocalAddresses(addresses)); this.trySigningIn = () => { if (isDDGDomain()) { @@ -394,8 +292,6 @@ class AndroidInterface extends InterfacePrototype { userName } }) => window.EmailInterface.storeCredentials(token, userName); - - this.attachTooltip = createAttachTooltip(this.getAlias); } } @@ -416,20 +312,26 @@ class AppleDeviceInterface extends InterfacePrototype { } = { shouldLog: false }) => { + if (isApp) { + await this.getCredentials(); + } + const signedIn = await this.isDeviceSignedIn(); if (signedIn) { - this.attachTooltip = createAttachTooltip(this.getAddresses, this.refreshAlias, {}); + await this.getAddresses(); notifyWebApp({ deviceSignedIn: { value: true, shouldLog } }); - scanForInputs(this); + forms.forEach(form => form.redecorateAllInputs); } else { this.trySigningIn(); } + + scanForInputs(this); }; this.getAddresses = async () => { @@ -437,6 +339,7 @@ class AppleDeviceInterface extends InterfacePrototype { const { addresses } = await wkSendAndWait('emailHandlerGetAddresses'); + this.storeLocalAddresses(addresses); return addresses; }; @@ -468,8 +371,6 @@ class AppleDeviceInterface extends InterfacePrototype { token, username: userName }); - - this.attachTooltip = createAttachTooltip(this.getAlias, this.refreshAlias); /** * PM endpoints */ @@ -488,6 +389,7 @@ class AppleDeviceInterface extends InterfacePrototype { * @param {{username: String, password: String}} credentials */ + this.storeCredentials = credentials => wkSend('pmHandlerStoreCredentials', credentials); /** * Gets a list of credentials for the current site @@ -496,7 +398,7 @@ class AppleDeviceInterface extends InterfacePrototype { this.getCredentials = () => wkSendAndWait('pmHandlerGetCredentials').then(response => { - console.log('rattone', response); + this.storeLocalCredentials(response.success); return response; }); /** @@ -512,6 +414,12 @@ class AppleDeviceInterface extends InterfacePrototype { console.log(response); return response; }); + /** + * Opens the native UI for managing passwords + */ + + + this.openManagePasswords = () => wkSend('pmHandlerOpenManagePasswords'); } } @@ -526,11 +434,17 @@ const DeviceInterface = (() => { module.exports = DeviceInterface; -},{"./DDGAutofill":1,"./appleDeviceUtils/appleDeviceUtils":5,"./autofill-utils":7,"./scanForInputs.js":11}],3:[function(require,module,exports){ +},{"./Form/selectors":5,"./UI/CredentialsAutofill":6,"./UI/EmailAutofill":7,"./appleDeviceUtils/appleDeviceUtils":10,"./autofill-utils":12,"./scanForInputs.js":15}],2:[function(require,module,exports){ "use strict"; +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + const FormAnalyzer = require('./FormAnalyzer'); +const { + PASSWORD_SELECTOR +} = require('./selectors'); + const { addInlineStyles, removeInlineStyles, @@ -538,7 +452,7 @@ const { isApp, setValue, isEventWithinDax -} = require('./autofill-utils'); +} = require('../autofill-utils'); const { daxBase64 @@ -563,14 +477,26 @@ const INLINE_AUTOFILLED_STYLES = { }; class Form { - constructor(form, input, attachTooltip) { + constructor(form, _input, DeviceInterface) { + _defineProperty(this, "autofillInput", (input, string) => { + setValue(input, string); + input.classList.add('ddg-autofilled'); + addInlineStyles(input, INLINE_AUTOFILLED_STYLES); // If the user changes the alias, remove the decoration + + input.addEventListener('input', this.removeAllHighlights, { + once: true + }); + }); + this.form = form; - this.formAnalyzer = new FormAnalyzer(form, input); - this.attachTooltip = attachTooltip; - this.relevantInputs = new Set(); + this.formAnalyzer = new FormAnalyzer(form, _input); + this.Device = DeviceInterface; + this.attachTooltip = DeviceInterface.attachTooltip; + this.emailInputs = new Set(); + this.passwordInputs = new Set(); this.touched = new Set(); this.listeners = new Set(); - this.addInput(input); + this.addInput(_input); this.tooltip = null; this.activeInput = null; this.intObs = new IntersectionObserver(entries => { @@ -617,6 +543,11 @@ class Form { }) => el.removeEventListener(type, fn)); }; + this.redecorateAllInputs = () => { + this.removeAllDecorations(); + this.execOnInputs(this.decorateInput); + }; + this.resetAllInputs = () => { this.execOnInputs(input => { setValue(input, ''); @@ -633,12 +564,19 @@ class Form { } execOnInputs(fn) { - this.relevantInputs.forEach(fn); + this.emailInputs.forEach(fn); + this.passwordInputs.forEach(fn); } addInput(input) { - this.relevantInputs.add(input); - if (this.formAnalyzer.autofillSignal > 0) this.decorateInput(input); + if (input.type === 'password') { + this.passwordInputs.add(input); + if (this.formAnalyzer.isLogin && this.Device.hasLocalCredentials) this.decorateInput(input); + } else { + this.emailInputs.add(input); + if (this.formAnalyzer.isSignup && this.Device.hasLocalAddresses) this.decorateInput(input); + } + return this; } @@ -690,15 +628,21 @@ class Form { return !this.touched.has(input) && this.areAllInputsEmpty() || isEventWithinDax(e, input); } - autofill(alias) { - this.execOnInputs(input => { - setValue(input, alias); - input.classList.add('ddg-autofilled'); - addInlineStyles(input, INLINE_AUTOFILLED_STYLES); // If the user changes the alias, remove the decoration + autofillEmail(alias) { + this.execOnInputs(input => !input.matches(PASSWORD_SELECTOR) && this.autofillInput(input, alias)); - input.addEventListener('input', this.removeAllHighlights, { - once: true - }); + if (this.tooltip) { + this.removeTooltip(); + } + } + + autofillCredentials(credentials) { + this.execOnInputs(input => { + if (input.matches(PASSWORD_SELECTOR)) { + this.autofillInput(input, credentials.password); + } else { + this.autofillInput(input, credentials.username); + } }); if (this.tooltip) { @@ -710,9 +654,14 @@ class Form { module.exports = Form; -},{"./FormAnalyzer":4,"./autofill-utils":7,"./logo-svg":9}],4:[function(require,module,exports){ +},{"../autofill-utils":12,"./FormAnalyzer":3,"./logo-svg":4,"./selectors":5}],3:[function(require,module,exports){ "use strict"; +const { + PASSWORD_SELECTOR, + SUBMIT_BUTTON_SELECTOR +} = require('./selectors'); + class FormAnalyzer { constructor(form, input) { this.form = form; @@ -725,6 +674,14 @@ class FormAnalyzer { return this; } + get isLogin() { + return this.autofillSignal < 0; + } + + get isSignup() { + return this.autofillSignal > 0; + } + increaseSignalBy(strength, signal) { this.autofillSignal += strength; this.signals.push("".concat(signal, ": +").concat(strength)); @@ -776,6 +733,27 @@ class FormAnalyzer { } evaluateElAttributes(el, signalStrength = 3, isInput = false) { + if (el.matches(PASSWORD_SELECTOR)) { + var _el$getAttribute, _el$getAttribute2; + + // These are explicit signals by the web author, so we weigh them heavily + if ((_el$getAttribute = el.getAttribute('autocomplete')) !== null && _el$getAttribute !== void 0 && _el$getAttribute.includes('current-password')) { + this.updateSignal({ + string: 'current-password', + strength: -20, + signalType: 'current-password' + }); + } + + if ((_el$getAttribute2 = el.getAttribute('autocomplete')) !== null && _el$getAttribute2 !== void 0 && _el$getAttribute2.includes('new-password')) { + this.updateSignal({ + string: 'new-password', + strength: 20, + signalType: 'new-password' + }); + } + } + Array.from(el.attributes).forEach(attr => { if (attr.name === 'style') return; const attributeString = "".concat(attr.name, "=").concat(attr.value); @@ -844,7 +822,7 @@ class FormAnalyzer { evaluateElement(el) { const string = this.getText(el); // check button contents - if (this.elementIs(el, 'INPUT') && ['submit', 'button'].includes(el.type) || this.elementIs(el, 'BUTTON') && el.type === 'submit' || (el.getAttribute('role') || '').toUpperCase() === 'BUTTON') { + if (el.matches(SUBMIT_BUTTON_SELECTOR)) { this.updateSignal({ string, strength: 2, @@ -891,9 +869,285 @@ class FormAnalyzer { module.exports = FormAnalyzer; +},{"./selectors":5}],4:[function(require,module,exports){ +"use strict"; + +const daxBase64 = 'data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgNDQgNDQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGxpbmVhckdyYWRpZW50IGlkPSJhIj48c3RvcCBvZmZzZXQ9Ii4wMSIgc3RvcC1jb2xvcj0iIzYxNzZiOSIvPjxzdG9wIG9mZnNldD0iLjY5IiBzdG9wLWNvbG9yPSIjMzk0YTlmIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImIiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTMuOTI5NyIgeDI9IjE3LjA3MiIgeGxpbms6aHJlZj0iI2EiIHkxPSIxNi4zOTgiIHkyPSIxNi4zOTgiLz48bGluZWFyR3JhZGllbnQgaWQ9ImMiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMjMuODExNSIgeDI9IjI2LjY3NTIiIHhsaW5rOmhyZWY9IiNhIiB5MT0iMTQuOTY3OSIgeTI9IjE0Ljk2NzkiLz48bWFzayBpZD0iZCIgaGVpZ2h0PSI0MCIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgd2lkdGg9IjQwIiB4PSIyIiB5PSIyIj48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0yMi4wMDAzIDQxLjA2NjljMTAuNTMwMiAwIDE5LjA2NjYtOC41MzY0IDE5LjA2NjYtMTkuMDY2NiAwLTEwLjUzMDMtOC41MzY0LTE5LjA2NjcxLTE5LjA2NjYtMTkuMDY2NzEtMTAuNTMwMyAwLTE5LjA2NjcxIDguNTM2NDEtMTkuMDY2NzEgMTkuMDY2NzEgMCAxMC41MzAyIDguNTM2NDEgMTkuMDY2NiAxOS4wNjY3MSAxOS4wNjY2eiIgZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9tYXNrPjxwYXRoIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0ibTIyIDQ0YzEyLjE1MDMgMCAyMi05Ljg0OTcgMjItMjIgMC0xMi4xNTAyNi05Ljg0OTctMjItMjItMjItMTIuMTUwMjYgMC0yMiA5Ljg0OTc0LTIyIDIyIDAgMTIuMTUwMyA5Ljg0OTc0IDIyIDIyIDIyeiIgZmlsbD0iI2RlNTgzMyIgZmlsbC1ydWxlPSJldmVub2RkIi8+PGcgbWFzaz0idXJsKCNkKSI+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJtMjYuMDgxMyA0MS42Mzg2Yy0uOTIwMy0xLjc4OTMtMS44MDAzLTMuNDM1Ni0yLjM0NjYtNC41MjQ2LTEuNDUyLTIuOTA3Ny0yLjkxMTQtNy4wMDctMi4yNDc3LTkuNjUwNy4xMjEtLjQ4MDMtMS4zNjc3LTE3Ljc4Njk5LTIuNDItMTguMzQ0MzItMS4xNjk3LS42MjMzMy0zLjcxMDctMS40NDQ2Ny01LjAyNy0xLjY2NDY3LS45MTY3LS4xNDY2Ni0xLjEyNTcuMTEtMS41MTA3LjE2ODY3LjM2My4wMzY2NyAyLjA5Ljg4NzMzIDIuNDIzNy45MzUtLjMzMzcuMjI3MzMtMS4zMi0uMDA3MzMtMS45NTA3LjI3MTMzLS4zMTkuMTQ2NjctLjU1NzMuNjg5MzQtLjU1Ljk0NiAxLjc5NjctLjE4MzMzIDQuNjA1NC0uMDAzNjYgNi4yNy43MzMyOS0xLjMyMzYuMTUwNC0zLjMzMy4zMTktNC4xOTgzLjc3MzctMi41MDggMS4zMi0zLjYxNTMgNC40MTEtMi45NTUzIDguMTE0My42NTYzIDMuNjk2IDMuNTY0IDE3LjE3ODQgNC40OTE2IDIxLjY4MS45MjQgNC40OTkgMTEuNTUzNyAzLjU1NjcgMTAuMDE3NC41NjF6IiBmaWxsPSIjZDVkN2Q4IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBkPSJtMjIuMjg2NSAyNi44NDM5Yy0uNjYgMi42NDM2Ljc5MiA2LjczOTMgMi4yNDc2IDkuNjUwNi40ODkxLjk3MjcgMS4yNDM4IDIuMzkyMSAyLjA1NTggMy45NjM3LTEuODk0LjQ2OTMtNi40ODk1IDEuMTI2NC05LjcxOTEgMC0uOTI0LTQuNDkxNy0zLjgzMTctMTcuOTc3Ny00LjQ5NTMtMjEuNjgxLS42Ni0zLjcwMzMgMC02LjM0NyAyLjUxNTMtNy42NjcuODYxNy0uNDU0NyAyLjA5MzctLjc4NDcgMy40MTM3LS45MzEzLTEuNjY0Ny0uNzQwNy0zLjYzNzQtMS4wMjY3LTUuNDQxNC0uODQzMzYtLjAwNzMtLjc2MjY3IDEuMzM4NC0uNzE4NjcgMS44NDQ0LTEuMDYzMzQtLjMzMzctLjA0NzY2LTEuMTYyNC0uNzk1NjYtMS41MjktLjgzMjMzIDIuMjg4My0uMzkyNDQgNC42NDIzLS4wMjEzOCA2LjY5OSAxLjA1NiAxLjA0ODYuNTYxIDEuNzg5MyAxLjE2MjMzIDIuMjQ3NiAxLjc5MzAzIDEuMTk1NC4yMjczIDIuMjUxNC42NiAyLjk0MDcgMS4zNDkzIDIuMTE5MyAyLjExNTcgNC4wMTEzIDYuOTUyIDMuMjE5MyA5LjczMTMtLjIyMzYuNzctLjczMzMgMS4zMzEtMS4zNzEzIDEuNzk2Ny0xLjIzOTMuOTAyLTEuMDE5My0xLjA0NS00LjEwMy45NzE3LS4zOTk3LjI2MDMtLjM5OTcgMi4yMjU2LS41MjQzIDIuNzA2eiIgZmlsbD0iI2ZmZiIvPjwvZz48ZyBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PHBhdGggZD0ibTE2LjY3MjQgMjAuMzU0Yy43Njc1IDAgMS4zODk2LS42MjIxIDEuMzg5Ni0xLjM4OTZzLS42MjIxLTEuMzg5Ny0xLjM4OTYtMS4zODk3LTEuMzg5Ny42MjIyLTEuMzg5NyAxLjM4OTcuNjIyMiAxLjM4OTYgMS4zODk3IDEuMzg5NnoiIGZpbGw9IiMyZDRmOGUiLz48cGF0aCBkPSJtMTcuMjkyNCAxOC44NjE3Yy4xOTg1IDAgLjM1OTQtLjE2MDguMzU5NC0uMzU5M3MtLjE2MDktLjM1OTMtLjM1OTQtLjM1OTNjLS4xOTg0IDAtLjM1OTMuMTYwOC0uMzU5My4zNTkzcy4xNjA5LjM1OTMuMzU5My4zNTkzeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Im0yNS45NTY4IDE5LjMzMTFjLjY1ODEgMCAxLjE5MTctLjUzMzUgMS4xOTE3LTEuMTkxNyAwLS42NTgxLS41MzM2LTEuMTkxNi0xLjE5MTctMS4xOTE2cy0xLjE5MTcuNTMzNS0xLjE5MTcgMS4xOTE2YzAgLjY1ODIuNTMzNiAxLjE5MTcgMS4xOTE3IDEuMTkxN3oiIGZpbGw9IiMyZDRmOGUiLz48cGF0aCBkPSJtMjYuNDg4MiAxOC4wNTExYy4xNzAxIDAgLjMwOC0uMTM3OS4zMDgtLjMwOHMtLjEzNzktLjMwOC0uMzA4LS4zMDgtLjMwOC4xMzc5LS4zMDguMzA4LjEzNzkuMzA4LjMwOC4zMDh6IiBmaWxsPSIjZmZmIi8+PHBhdGggZD0ibTE3LjA3MiAxNC45NDJzLTEuMDQ4Ni0uNDc2Ni0yLjA2NDMuMTY1Yy0xLjAxNTcuNjM4LS45NzkgMS4yOTA3LS45NzkgMS4yOTA3cy0uNTM5LTEuMjAyNy44OTgzLTEuNzkzYzEuNDQxLS41ODY3IDIuMTQ1LjMzNzMgMi4xNDUuMzM3M3oiIGZpbGw9InVybCgjYikiLz48cGF0aCBkPSJtMjYuNjc1MiAxNC44NDY3cy0uNzUxNy0uNDI5LTEuMzM4My0uNDIxN2MtMS4xOTkuMDE0Ny0xLjUyNTQuNTQyNy0xLjUyNTQuNTQyN3MuMjAxNy0xLjI2MTQgMS43MzQ0LTEuMDA4NGMuNDk5Ny4wOTE0LjkyMjMuNDIzNCAxLjEyOTMuODg3NHoiIGZpbGw9InVybCgjYykiLz48cGF0aCBkPSJtMjAuOTI1OCAyNC4zMjFjLjEzOTMtLjg0MzMgMi4zMS0yLjQzMSAzLjg1LTIuNTMgMS41NC0uMDk1MyAyLjAxNjctLjA3MzMgMy4zLS4zODEzIDEuMjg3LS4zMDQzIDQuNTk4LTEuMTI5MyA1LjUxMS0xLjU1NDcuOTE2Ny0uNDIxNiA0LjgwMzMuMjA5IDIuMDY0MyAxLjczOC0xLjE4NDMuNjYzNy00LjM3OCAxLjg4MS02LjY2MjMgMi41NjMtMi4yODA3LjY4Mi0zLjY2My0uNjUyNi00LjQyMi40Njk0LS42MDEzLjg5MS0uMTIxIDIuMTEyIDIuNjAzMyAyLjM2NSAzLjY4MTQuMzQxIDcuMjA4Ny0xLjY1NzQgNy41OTc0LS41OTQuMzg4NiAxLjA2MzMtMy4xNjA3IDIuMzgzMy01LjMyNCAyLjQyNzMtMi4xNjM0LjA0MDMtNi41MTk0LTEuNDMtNy4xNzItMS44ODQ3LS42NTY0LS40NTEtMS41MjU0LTEuNTE0My0xLjM0NTctMi42MTh6IiBmaWxsPSIjZmRkMjBhIi8+PHBhdGggZD0ibTI4Ljg4MjUgMzEuODM4NmMtLjc3NzMtLjE3MjQtNC4zMTIgMi41MDA2LTQuMzEyIDIuNTAwNmguMDAzN2wtLjE2NSAyLjA1MzRzNC4wNDA2IDEuNjUzNiA0LjczIDEuMzk3Yy42ODkzLS4yNjQuNTE3LTUuNzc1LS4yNTY3LTUuOTUxem0tMTEuNTQ2MyAxLjAzNGMuMDg0My0xLjExODQgNS4yNTQzIDEuNjQyNiA1LjI1NDMgMS42NDI2bC4wMDM3LS4wMDM2LjI1NjYgMi4xNTZzLTQuMzA4MyAyLjU4MTMtNC45MTMzIDIuMjM2NmMtLjYwMTMtLjM0NDYtLjY4OTMtNC45MDk2LS42MDEzLTYuMDMxNnoiIGZpbGw9IiM2NWJjNDYiLz48cGF0aCBkPSJtMjEuMzQgMzQuODA0OWMwIDEuODA3Ny0uMjYwNCAyLjU4NS41MTMzIDIuNzU3NC43NzczLjE3MjMgMi4yNDAzIDAgMi43NjEtLjM0NDcuNTEzMy0uMzQ0Ny4wODQzLTIuNjY5My0uMDg4LTMuMTAycy0zLjE5LS4wODgtMy4xOS42ODkzeiIgZmlsbD0iIzQzYTI0NCIvPjxwYXRoIGQ9Im0yMS42NzAxIDM0LjQwNTFjMCAxLjgwNzYtLjI2MDQgMi41ODEzLjUxMzMgMi43NTM2Ljc3MzcuMTc2IDIuMjM2NyAwIDIuNzU3My0uMzQ0Ni41MTctLjM0NDcuMDg4LTIuNjY5NC0uMDg0My0zLjEwMi0uMTcyMy0uNDMyNy0zLjE5LS4wODQ0LTMuMTkuNjg5M3oiIGZpbGw9IiM2NWJjNDYiLz48cGF0aCBkPSJtMjIuMDAwMiA0MC40NDgxYzEwLjE4ODUgMCAxOC40NDc5LTguMjU5NCAxOC40NDc5LTE4LjQ0NzlzLTguMjU5NC0xOC40NDc5NS0xOC40NDc5LTE4LjQ0Nzk1LTE4LjQ0Nzk1IDguMjU5NDUtMTguNDQ3OTUgMTguNDQ3OTUgOC4yNTk0NSAxOC40NDc5IDE4LjQ0Nzk1IDE4LjQ0Nzl6bTAgMS43MTg3YzExLjEzNzcgMCAyMC4xNjY2LTkuMDI4OSAyMC4xNjY2LTIwLjE2NjYgMC0xMS4xMzc4LTkuMDI4OS0yMC4xNjY3LTIwLjE2NjYtMjAuMTY2Ny0xMS4xMzc4IDAtMjAuMTY2NyA5LjAyODktMjAuMTY2NyAyMC4xNjY3IDAgMTEuMTM3NyA5LjAyODkgMjAuMTY2NiAyMC4xNjY3IDIwLjE2NjZ6IiBmaWxsPSIjZmZmIi8+PC9nPjwvc3ZnPg=='; +module.exports = { + daxBase64 +}; + },{}],5:[function(require,module,exports){ "use strict"; +const EMAIL_SELECTOR = "\n input:not([type])[name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=\"\"][name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=text][name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input:not([type])[id*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input:not([type])[placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=\"\"][id*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=text][placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=\"\"][placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input:not([type])[placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=email]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=text][aria-label*=mail i],\n input:not([type])[aria-label*=mail i],\n input[type=text][placeholder*=mail i]:not([readonly])\n"; +const PASSWORD_SELECTOR = "input[type=password]:not([autocomplete*=cc]):not([autocomplete=one-time-code])"; +const FIELD_SELECTOR = [PASSWORD_SELECTOR, EMAIL_SELECTOR].join(', '); +const SUBMIT_BUTTON_SELECTOR = 'input[type=submit], input[type=button], button[type=submit], [role=button]'; +module.exports = { + EMAIL_SELECTOR, + PASSWORD_SELECTOR, + FIELD_SELECTOR, + SUBMIT_BUTTON_SELECTOR +}; + +},{}],6:[function(require,module,exports){ +"use strict"; + +const { + isApp, + safeExecute, + escapeXML +} = require('../autofill-utils'); + +const Tooltip = require('./Tooltip'); + +class CredentialsAutofill extends Tooltip { + constructor(input, associatedForm, Interface) { + super(input, associatedForm, Interface); + this.credentials = this.interface.getLocalCredentials(); + const includeStyles = isApp ? "") : ""); + this.shadow.innerHTML = "\n".concat(includeStyles, "\n
\n \n
"); + this.wrapper = this.shadow.querySelector('.wrapper'); + this.tooltip = this.shadow.querySelector('.tooltip'); + this.autofillButton = this.shadow.querySelector('.js-autofill-button'); + this.autofillButton.addEventListener('click', e => { + if (!e.isTrusted) return; + e.stopImmediatePropagation(); + safeExecute(this.autofillButton, () => { + this.interface.getAutofillCredentials().then(({ + success, + error + }) => { + if (success) this.associatedForm.autofillCredentials(success); + }); + }); + }); + this.init(); + } + +} + +module.exports = CredentialsAutofill; + +},{"../autofill-utils":12,"./Tooltip":8,"./styles/DDGAutofill-styles.js":9}],7:[function(require,module,exports){ +"use strict"; + +const { + isApp, + formatAddress, + safeExecute, + escapeXML +} = require('../autofill-utils'); + +const Tooltip = require('./Tooltip'); + +class EmailAutofill extends Tooltip { + constructor(input, associatedForm, Interface) { + super(input, associatedForm, Interface); + this.addresses = this.interface.getLocalAddresses(); + const includeStyles = isApp ? "") : ""); + this.shadow.innerHTML = "\n".concat(includeStyles, "\n
\n \n
"); + this.wrapper = this.shadow.querySelector('.wrapper'); + this.tooltip = this.shadow.querySelector('.tooltip'); + this.usePersonalButton = this.shadow.querySelector('.js-use-personal'); + this.usePrivateButton = this.shadow.querySelector('.js-use-private'); + this.addressEl = this.shadow.querySelector('.js-address'); + + this.updateAddresses = addresses => { + if (addresses) { + this.addresses = addresses; + this.addressEl.textContent = formatAddress(addresses.personalAddress); + } + }; + + this.usePersonalButton.addEventListener('click', e => { + if (!e.isTrusted) return; + e.stopImmediatePropagation(); + safeExecute(this.usePersonalButton, () => { + this.associatedForm.autofillEmail(formatAddress(this.addresses.personalAddress)); + }); + }); + this.usePrivateButton.addEventListener('click', e => { + if (!e.isTrusted) return; + e.stopImmediatePropagation(); + safeExecute(this.usePersonalButton, () => { + this.associatedForm.autofillEmail(formatAddress(this.addresses.privateAddress)); + this.interface.refreshAlias(); + }); + }); // Get the alias from the extension + + this.interface.getAddresses().then(this.updateAddresses); + this.init(); + } + +} + +module.exports = EmailAutofill; + +},{"../autofill-utils":12,"./Tooltip":8,"./styles/DDGAutofill-styles.js":9}],8:[function(require,module,exports){ +"use strict"; + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +const { + getDaxBoundingBox +} = require('../autofill-utils'); + +const updatePosition = function ({ + left, + top +}) { + const shadow = this.shadow; // If the stylesheet is not loaded wait for load (Chrome bug) + + if (!shadow.styleSheets.length) return this.stylesheet.addEventListener('load', this.checkPosition); + this.left = left; + this.top = top; + + if (this.transformRuleIndex && shadow.styleSheets[this.transformRuleIndex]) { + // If we have already set the rule, remove it… + shadow.styleSheets[0].deleteRule(this.transformRuleIndex); + } else { + // …otherwise, set the index as the very last rule + this.transformRuleIndex = shadow.styleSheets[0].rules.length; + } + + const newRule = ".wrapper {transform: translate(".concat(left, "px, ").concat(top, "px);}"); + shadow.styleSheets[0].insertRule(newRule, this.transformRuleIndex); +}; + +const checkPosition = function () { + if (this.animationFrame) { + window.cancelAnimationFrame(this.animationFrame); + } + + this.animationFrame = window.requestAnimationFrame(() => { + const { + left, + bottom + } = getDaxBoundingBox(this.input); + + if (left !== this.left || bottom !== this.top) { + this.updatePosition({ + left, + top: bottom + }); + } + + this.animationFrame = null; + }); +}; + +const ensureIsLastInDOM = function () { + this.count = this.count || 0; // If DDG el is not the last in the doc, move it there + + if (document.body.lastElementChild !== this.host) { + // Try up to 5 times to avoid infinite loop in case someone is doing the same + if (this.count < 15) { + this.lift(); + this.append(); + this.checkPosition(); + this.count++; + } else { + // Remove the tooltip from the form to cleanup listeners and observers + this.associatedForm.removeTooltip(); + console.info("DDG autofill bailing out"); + } + } +}; + +class Tooltip { + constructor(input, associatedForm, Interface) { + _defineProperty(this, "checkPosition", checkPosition.bind(this)); + + _defineProperty(this, "updatePosition", updatePosition.bind(this)); + + _defineProperty(this, "ensureIsLastInDOM", ensureIsLastInDOM.bind(this)); + + _defineProperty(this, "resObs", new ResizeObserver(entries => entries.forEach(this.checkPosition))); + + _defineProperty(this, "mutObs", new MutationObserver(mutationList => { + for (const mutationRecord of mutationList) { + if (mutationRecord.type === 'childList') { + // Only check added nodes + mutationRecord.addedNodes.forEach(el => { + if (el.nodeName === 'DDG-AUTOFILL') return; + this.ensureIsLastInDOM(); + }); + } + } + + this.checkPosition(); + })); + + this.shadow = document.createElement('ddg-autofill').attachShadow({ + mode: 'closed' + }); + this.host = this.shadow.host; + this.input = input; + this.associatedForm = associatedForm; + this.interface = Interface; + } + + append() { + document.body.appendChild(this.host); + } + + remove() { + window.removeEventListener('scroll', this.checkPosition, { + passive: true, + capture: true + }); + this.resObs.disconnect(); + this.mutObs.disconnect(); + this.lift(); + } + + lift() { + this.left = null; + this.top = null; + document.body.removeChild(this.host); + } + + init() { + this.animationFrame = null; + this.top = 0; + this.left = 0; + this.transformRuleIndex = null; + this.stylesheet = this.shadow.querySelector('link, style'); // Un-hide once the style is loaded, to avoid flashing unstyled content + + this.stylesheet.addEventListener('load', () => this.tooltip.removeAttribute('hidden')); + this.append(); + this.resObs.observe(document.body); + this.mutObs.observe(document.body, { + childList: true, + subtree: true, + attributes: true + }); + window.addEventListener('scroll', this.checkPosition, { + passive: true, + capture: true + }); + } + + setupStylesheet() { + this.stylesheet = this.shadow.querySelector('link, style'); // Un-hide once the style is loaded, to avoid flashing unstyled content + + this.stylesheet.addEventListener('load', () => this.tooltip.removeAttribute('hidden')); + } + +} + +module.exports = Tooltip; + +},{"../autofill-utils":12}],9:[function(require,module,exports){ +"use strict"; + +module.exports = "\n.wrapper *, .wrapper *::before, .wrapper *::after {\n box-sizing: border-box;\n}\n.wrapper {\n position: fixed;\n top: 0;\n left: 0;\n padding: 0;\n font-family: 'DDG_ProximaNova', 'Proxima Nova', -apple-system,\n BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',\n 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;\n -webkit-font-smoothing: antialiased;\n /* move it offscreen to avoid flashing */\n transform: translate(-1000px);\n z-index: 2147483647;\n}\n.tooltip {\n position: absolute;\n top: calc(100% + 6px);\n right: calc(100% - 46px);\n width: 300px;\n max-width: calc(100vw - 25px);\n padding: 8px;\n border: 1px solid #D0D0D0;\n border-radius: 10px;\n background-color: #FFFFFF;\n font-size: 14px;\n color: #333333;\n line-height: 1.3;\n box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);\n z-index: 2147483647;\n}\n.tooltip::before,\n.tooltip::after {\n content: \"\";\n width: 0;\n height: 0;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n display: block;\n border-bottom: 8px solid #D0D0D0;\n position: absolute;\n right: 20px;\n}\n.tooltip::before {\n border-bottom-color: #D0D0D0;\n top: -9px;\n}\n.tooltip::after {\n border-bottom-color: #FFFFFF;\n top: -8px;\n}\n.tooltip__button {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: flex-start;\n width: 100%;\n padding: 4px 8px 7px;\n font-family: inherit;\n font-size: 14px;\n background: transparent;\n border: none;\n border-radius: 6px;\n}\n.tooltip__button:hover {\n background-color: #3969EF;\n color: #FFFFFF;\n}\n.tooltip__button__primary-text {\n font-weight: bold;\n}\n.tooltip__button__secondary-text {\n font-size: 12px;\n}\n"; + +},{}],10:[function(require,module,exports){ +"use strict"; + // Do not remove -- Apple devices change this when they support modern webkit messaging let hasModernWebkitAPI = false; // INJECT hasModernWebkitAPI HERE // The native layer will inject a randomised secret here and use it to verify the origin @@ -1009,7 +1263,7 @@ module.exports = { wkSendAndWait }; -},{"./captureDdgGlobals":6}],6:[function(require,module,exports){ +},{"./captureDdgGlobals":11}],11:[function(require,module,exports){ "use strict"; // Capture the globals we need on page start @@ -1035,7 +1289,7 @@ const secretGlobals = { }; module.exports = secretGlobals; -},{}],7:[function(require,module,exports){ +},{}],12:[function(require,module,exports){ "use strict"; let isApp = false; // Do not modify or remove the next line -- the app code will replace it with `isApp = true;` @@ -1211,7 +1465,7 @@ module.exports = { escapeXML }; -},{}],8:[function(require,module,exports){ +},{}],13:[function(require,module,exports){ "use strict"; (() => { @@ -1240,15 +1494,7 @@ module.exports = { } })(); -},{"./DeviceInterface":2,"./requestIdleCallback":10}],9:[function(require,module,exports){ -"use strict"; - -const daxBase64 = 'data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgNDQgNDQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGxpbmVhckdyYWRpZW50IGlkPSJhIj48c3RvcCBvZmZzZXQ9Ii4wMSIgc3RvcC1jb2xvcj0iIzYxNzZiOSIvPjxzdG9wIG9mZnNldD0iLjY5IiBzdG9wLWNvbG9yPSIjMzk0YTlmIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImIiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTMuOTI5NyIgeDI9IjE3LjA3MiIgeGxpbms6aHJlZj0iI2EiIHkxPSIxNi4zOTgiIHkyPSIxNi4zOTgiLz48bGluZWFyR3JhZGllbnQgaWQ9ImMiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMjMuODExNSIgeDI9IjI2LjY3NTIiIHhsaW5rOmhyZWY9IiNhIiB5MT0iMTQuOTY3OSIgeTI9IjE0Ljk2NzkiLz48bWFzayBpZD0iZCIgaGVpZ2h0PSI0MCIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgd2lkdGg9IjQwIiB4PSIyIiB5PSIyIj48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0yMi4wMDAzIDQxLjA2NjljMTAuNTMwMiAwIDE5LjA2NjYtOC41MzY0IDE5LjA2NjYtMTkuMDY2NiAwLTEwLjUzMDMtOC41MzY0LTE5LjA2NjcxLTE5LjA2NjYtMTkuMDY2NzEtMTAuNTMwMyAwLTE5LjA2NjcxIDguNTM2NDEtMTkuMDY2NzEgMTkuMDY2NzEgMCAxMC41MzAyIDguNTM2NDEgMTkuMDY2NiAxOS4wNjY3MSAxOS4wNjY2eiIgZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9tYXNrPjxwYXRoIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0ibTIyIDQ0YzEyLjE1MDMgMCAyMi05Ljg0OTcgMjItMjIgMC0xMi4xNTAyNi05Ljg0OTctMjItMjItMjItMTIuMTUwMjYgMC0yMiA5Ljg0OTc0LTIyIDIyIDAgMTIuMTUwMyA5Ljg0OTc0IDIyIDIyIDIyeiIgZmlsbD0iI2RlNTgzMyIgZmlsbC1ydWxlPSJldmVub2RkIi8+PGcgbWFzaz0idXJsKCNkKSI+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJtMjYuMDgxMyA0MS42Mzg2Yy0uOTIwMy0xLjc4OTMtMS44MDAzLTMuNDM1Ni0yLjM0NjYtNC41MjQ2LTEuNDUyLTIuOTA3Ny0yLjkxMTQtNy4wMDctMi4yNDc3LTkuNjUwNy4xMjEtLjQ4MDMtMS4zNjc3LTE3Ljc4Njk5LTIuNDItMTguMzQ0MzItMS4xNjk3LS42MjMzMy0zLjcxMDctMS40NDQ2Ny01LjAyNy0xLjY2NDY3LS45MTY3LS4xNDY2Ni0xLjEyNTcuMTEtMS41MTA3LjE2ODY3LjM2My4wMzY2NyAyLjA5Ljg4NzMzIDIuNDIzNy45MzUtLjMzMzcuMjI3MzMtMS4zMi0uMDA3MzMtMS45NTA3LjI3MTMzLS4zMTkuMTQ2NjctLjU1NzMuNjg5MzQtLjU1Ljk0NiAxLjc5NjctLjE4MzMzIDQuNjA1NC0uMDAzNjYgNi4yNy43MzMyOS0xLjMyMzYuMTUwNC0zLjMzMy4zMTktNC4xOTgzLjc3MzctMi41MDggMS4zMi0zLjYxNTMgNC40MTEtMi45NTUzIDguMTE0My42NTYzIDMuNjk2IDMuNTY0IDE3LjE3ODQgNC40OTE2IDIxLjY4MS45MjQgNC40OTkgMTEuNTUzNyAzLjU1NjcgMTAuMDE3NC41NjF6IiBmaWxsPSIjZDVkN2Q4IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBkPSJtMjIuMjg2NSAyNi44NDM5Yy0uNjYgMi42NDM2Ljc5MiA2LjczOTMgMi4yNDc2IDkuNjUwNi40ODkxLjk3MjcgMS4yNDM4IDIuMzkyMSAyLjA1NTggMy45NjM3LTEuODk0LjQ2OTMtNi40ODk1IDEuMTI2NC05LjcxOTEgMC0uOTI0LTQuNDkxNy0zLjgzMTctMTcuOTc3Ny00LjQ5NTMtMjEuNjgxLS42Ni0zLjcwMzMgMC02LjM0NyAyLjUxNTMtNy42NjcuODYxNy0uNDU0NyAyLjA5MzctLjc4NDcgMy40MTM3LS45MzEzLTEuNjY0Ny0uNzQwNy0zLjYzNzQtMS4wMjY3LTUuNDQxNC0uODQzMzYtLjAwNzMtLjc2MjY3IDEuMzM4NC0uNzE4NjcgMS44NDQ0LTEuMDYzMzQtLjMzMzctLjA0NzY2LTEuMTYyNC0uNzk1NjYtMS41MjktLjgzMjMzIDIuMjg4My0uMzkyNDQgNC42NDIzLS4wMjEzOCA2LjY5OSAxLjA1NiAxLjA0ODYuNTYxIDEuNzg5MyAxLjE2MjMzIDIuMjQ3NiAxLjc5MzAzIDEuMTk1NC4yMjczIDIuMjUxNC42NiAyLjk0MDcgMS4zNDkzIDIuMTE5MyAyLjExNTcgNC4wMTEzIDYuOTUyIDMuMjE5MyA5LjczMTMtLjIyMzYuNzctLjczMzMgMS4zMzEtMS4zNzEzIDEuNzk2Ny0xLjIzOTMuOTAyLTEuMDE5My0xLjA0NS00LjEwMy45NzE3LS4zOTk3LjI2MDMtLjM5OTcgMi4yMjU2LS41MjQzIDIuNzA2eiIgZmlsbD0iI2ZmZiIvPjwvZz48ZyBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PHBhdGggZD0ibTE2LjY3MjQgMjAuMzU0Yy43Njc1IDAgMS4zODk2LS42MjIxIDEuMzg5Ni0xLjM4OTZzLS42MjIxLTEuMzg5Ny0xLjM4OTYtMS4zODk3LTEuMzg5Ny42MjIyLTEuMzg5NyAxLjM4OTcuNjIyMiAxLjM4OTYgMS4zODk3IDEuMzg5NnoiIGZpbGw9IiMyZDRmOGUiLz48cGF0aCBkPSJtMTcuMjkyNCAxOC44NjE3Yy4xOTg1IDAgLjM1OTQtLjE2MDguMzU5NC0uMzU5M3MtLjE2MDktLjM1OTMtLjM1OTQtLjM1OTNjLS4xOTg0IDAtLjM1OTMuMTYwOC0uMzU5My4zNTkzcy4xNjA5LjM1OTMuMzU5My4zNTkzeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Im0yNS45NTY4IDE5LjMzMTFjLjY1ODEgMCAxLjE5MTctLjUzMzUgMS4xOTE3LTEuMTkxNyAwLS42NTgxLS41MzM2LTEuMTkxNi0xLjE5MTctMS4xOTE2cy0xLjE5MTcuNTMzNS0xLjE5MTcgMS4xOTE2YzAgLjY1ODIuNTMzNiAxLjE5MTcgMS4xOTE3IDEuMTkxN3oiIGZpbGw9IiMyZDRmOGUiLz48cGF0aCBkPSJtMjYuNDg4MiAxOC4wNTExYy4xNzAxIDAgLjMwOC0uMTM3OS4zMDgtLjMwOHMtLjEzNzktLjMwOC0uMzA4LS4zMDgtLjMwOC4xMzc5LS4zMDguMzA4LjEzNzkuMzA4LjMwOC4zMDh6IiBmaWxsPSIjZmZmIi8+PHBhdGggZD0ibTE3LjA3MiAxNC45NDJzLTEuMDQ4Ni0uNDc2Ni0yLjA2NDMuMTY1Yy0xLjAxNTcuNjM4LS45NzkgMS4yOTA3LS45NzkgMS4yOTA3cy0uNTM5LTEuMjAyNy44OTgzLTEuNzkzYzEuNDQxLS41ODY3IDIuMTQ1LjMzNzMgMi4xNDUuMzM3M3oiIGZpbGw9InVybCgjYikiLz48cGF0aCBkPSJtMjYuNjc1MiAxNC44NDY3cy0uNzUxNy0uNDI5LTEuMzM4My0uNDIxN2MtMS4xOTkuMDE0Ny0xLjUyNTQuNTQyNy0xLjUyNTQuNTQyN3MuMjAxNy0xLjI2MTQgMS43MzQ0LTEuMDA4NGMuNDk5Ny4wOTE0LjkyMjMuNDIzNCAxLjEyOTMuODg3NHoiIGZpbGw9InVybCgjYykiLz48cGF0aCBkPSJtMjAuOTI1OCAyNC4zMjFjLjEzOTMtLjg0MzMgMi4zMS0yLjQzMSAzLjg1LTIuNTMgMS41NC0uMDk1MyAyLjAxNjctLjA3MzMgMy4zLS4zODEzIDEuMjg3LS4zMDQzIDQuNTk4LTEuMTI5MyA1LjUxMS0xLjU1NDcuOTE2Ny0uNDIxNiA0LjgwMzMuMjA5IDIuMDY0MyAxLjczOC0xLjE4NDMuNjYzNy00LjM3OCAxLjg4MS02LjY2MjMgMi41NjMtMi4yODA3LjY4Mi0zLjY2My0uNjUyNi00LjQyMi40Njk0LS42MDEzLjg5MS0uMTIxIDIuMTEyIDIuNjAzMyAyLjM2NSAzLjY4MTQuMzQxIDcuMjA4Ny0xLjY1NzQgNy41OTc0LS41OTQuMzg4NiAxLjA2MzMtMy4xNjA3IDIuMzgzMy01LjMyNCAyLjQyNzMtMi4xNjM0LjA0MDMtNi41MTk0LTEuNDMtNy4xNzItMS44ODQ3LS42NTY0LS40NTEtMS41MjU0LTEuNTE0My0xLjM0NTctMi42MTh6IiBmaWxsPSIjZmRkMjBhIi8+PHBhdGggZD0ibTI4Ljg4MjUgMzEuODM4NmMtLjc3NzMtLjE3MjQtNC4zMTIgMi41MDA2LTQuMzEyIDIuNTAwNmguMDAzN2wtLjE2NSAyLjA1MzRzNC4wNDA2IDEuNjUzNiA0LjczIDEuMzk3Yy42ODkzLS4yNjQuNTE3LTUuNzc1LS4yNTY3LTUuOTUxem0tMTEuNTQ2MyAxLjAzNGMuMDg0My0xLjExODQgNS4yNTQzIDEuNjQyNiA1LjI1NDMgMS42NDI2bC4wMDM3LS4wMDM2LjI1NjYgMi4xNTZzLTQuMzA4MyAyLjU4MTMtNC45MTMzIDIuMjM2NmMtLjYwMTMtLjM0NDYtLjY4OTMtNC45MDk2LS42MDEzLTYuMDMxNnoiIGZpbGw9IiM2NWJjNDYiLz48cGF0aCBkPSJtMjEuMzQgMzQuODA0OWMwIDEuODA3Ny0uMjYwNCAyLjU4NS41MTMzIDIuNzU3NC43NzczLjE3MjMgMi4yNDAzIDAgMi43NjEtLjM0NDcuNTEzMy0uMzQ0Ny4wODQzLTIuNjY5My0uMDg4LTMuMTAycy0zLjE5LS4wODgtMy4xOS42ODkzeiIgZmlsbD0iIzQzYTI0NCIvPjxwYXRoIGQ9Im0yMS42NzAxIDM0LjQwNTFjMCAxLjgwNzYtLjI2MDQgMi41ODEzLjUxMzMgMi43NTM2Ljc3MzcuMTc2IDIuMjM2NyAwIDIuNzU3My0uMzQ0Ni41MTctLjM0NDcuMDg4LTIuNjY5NC0uMDg0My0zLjEwMi0uMTcyMy0uNDMyNy0zLjE5LS4wODQ0LTMuMTkuNjg5M3oiIGZpbGw9IiM2NWJjNDYiLz48cGF0aCBkPSJtMjIuMDAwMiA0MC40NDgxYzEwLjE4ODUgMCAxOC40NDc5LTguMjU5NCAxOC40NDc5LTE4LjQ0NzlzLTguMjU5NC0xOC40NDc5NS0xOC40NDc5LTE4LjQ0Nzk1LTE4LjQ0Nzk1IDguMjU5NDUtMTguNDQ3OTUgMTguNDQ3OTUgOC4yNTk0NSAxOC40NDc5IDE4LjQ0Nzk1IDE4LjQ0Nzl6bTAgMS43MTg3YzExLjEzNzcgMCAyMC4xNjY2LTkuMDI4OSAyMC4xNjY2LTIwLjE2NjYgMC0xMS4xMzc4LTkuMDI4OS0yMC4xNjY3LTIwLjE2NjYtMjAuMTY2Ny0xMS4xMzc4IDAtMjAuMTY2NyA5LjAyODktMjAuMTY2NyAyMC4xNjY3IDAgMTEuMTM3NyA5LjAyODkgMjAuMTY2NiAyMC4xNjY3IDIwLjE2NjZ6IiBmaWxsPSIjZmZmIi8+PC9nPjwvc3ZnPg=='; -module.exports = { - daxBase64 -}; - -},{}],10:[function(require,module,exports){ +},{"./DeviceInterface":1,"./requestIdleCallback":14}],14:[function(require,module,exports){ "use strict"; /*! @@ -1287,20 +1533,22 @@ window.cancelIdleCallback = window.cancelIdleCallback || function (id) { clearTimeout(id); }; -},{}],11:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ "use strict"; -const Form = require('./Form'); +const Form = require('./Form/Form'); const { notifyWebApp -} = require('./autofill-utils'); // Accepts the DeviceInterface as an explicit dependency +} = require('./autofill-utils'); + +const { + FIELD_SELECTOR +} = require('./Form/selectors'); +const forms = new Map(); // Accepts the DeviceInterface as an explicit dependency const scanForInputs = DeviceInterface => { - const forms = new Map(); - const EMAIL_SELECTOR = "\n input:not([type])[name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=\"\"][name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=text][name*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input:not([type])[id*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input:not([type])[placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=\"\"][id*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=text][placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=\"\"][placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input:not([type])[placeholder*=mail i]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=email]:not([readonly]):not([disabled]):not([hidden]):not([aria-hidden=true]),\n input[type=text][aria-label*=mail i],\n input:not([type])[aria-label*=mail i],\n input[type=text][placeholder*=mail i]:not([readonly])\n "; - const addInput = input => { const parentForm = input.form; @@ -1308,15 +1556,15 @@ const scanForInputs = DeviceInterface => { // If we've already met the form, add the input forms.get(parentForm).addInput(input); } else { - forms.set(parentForm || input, new Form(parentForm, input, DeviceInterface.attachTooltip)); + forms.set(parentForm || input, new Form(parentForm, input, DeviceInterface)); } }; const findEligibleInput = context => { - if (context.nodeName === 'INPUT' && context.matches(EMAIL_SELECTOR)) { + if (context.nodeName === 'INPUT' && context.matches(FIELD_SELECTOR)) { addInput(context); } else { - context.querySelectorAll(EMAIL_SELECTOR).forEach(addInput); + context.querySelectorAll(FIELD_SELECTOR).forEach(addInput); } }; // For all DOM mutations, search for new eligible inputs and update existing inputs positions @@ -1363,11 +1611,9 @@ const scanForInputs = DeviceInterface => { }); }; -module.exports = scanForInputs; - -},{"./Form":3,"./autofill-utils":7}],12:[function(require,module,exports){ -"use strict"; - -module.exports = "\n.wrapper *, .wrapper *::before, .wrapper *::after {\n box-sizing: border-box;\n}\n.wrapper {\n position: fixed;\n top: 0;\n left: 0;\n padding: 0;\n font-family: 'DDG_ProximaNova', 'Proxima Nova', -apple-system,\n BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',\n 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;\n -webkit-font-smoothing: antialiased;\n /* move it offscreen to avoid flashing */\n transform: translate(-1000px);\n z-index: 2147483647;\n}\n.tooltip {\n position: absolute;\n top: calc(100% + 6px);\n right: calc(100% - 46px);\n width: 300px;\n max-width: calc(100vw - 25px);\n padding: 8px;\n border: 1px solid #D0D0D0;\n border-radius: 10px;\n background-color: #FFFFFF;\n font-size: 14px;\n color: #333333;\n line-height: 1.3;\n box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);\n z-index: 2147483647;\n}\n.tooltip::before,\n.tooltip::after {\n content: \"\";\n width: 0;\n height: 0;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n display: block;\n border-bottom: 8px solid #D0D0D0;\n position: absolute;\n right: 20px;\n}\n.tooltip::before {\n border-bottom-color: #D0D0D0;\n top: -9px;\n}\n.tooltip::after {\n border-bottom-color: #FFFFFF;\n top: -8px;\n}\n.tooltip__button {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: flex-start;\n width: 100%;\n padding: 4px 8px 7px;\n font-family: inherit;\n font-size: 14px;\n background: transparent;\n border: none;\n border-radius: 6px;\n}\n.tooltip__button:hover {\n background-color: #3969EF;\n color: #FFFFFF;\n}\n.tooltip__button__primary-text {\n font-weight: bold;\n}\n.tooltip__button__secondary-text {\n font-size: 12px;\n}\n"; +module.exports = { + scanForInputs, + forms +}; -},{}]},{},[8]); +},{"./Form/Form":2,"./Form/selectors":5,"./autofill-utils":12}]},{},[13]); diff --git a/src/Form/Form.js b/src/Form/Form.js index 6a4c7d236..e5f012c86 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -1,5 +1,5 @@ const FormAnalyzer = require('./FormAnalyzer') -const {PASSWORD_SELECTOR} = require("./selectors"); +const {PASSWORD_SELECTOR} = require('./selectors') const {addInlineStyles, removeInlineStyles, isDDGApp, isApp, setValue, isEventWithinDax} = require('../autofill-utils') const {daxBase64} = require('./logo-svg') @@ -97,7 +97,6 @@ class Form { if (this.formAnalyzer.isLogin && this.Device.hasLocalCredentials) this.decorateInput(input) } else { this.emailInputs.add(input) - if (this.formAnalyzer.isSignup) this.decorateInput(input) if (this.formAnalyzer.isSignup && this.Device.hasLocalAddresses) this.decorateInput(input) } diff --git a/src/UI/CredentialsAutofill.js b/src/UI/CredentialsAutofill.js index 938c5df11..e3575ff48 100644 --- a/src/UI/CredentialsAutofill.js +++ b/src/UI/CredentialsAutofill.js @@ -1,6 +1,5 @@ const { isApp, - formatAddress, safeExecute, escapeXML } = require('../autofill-utils') From 7da0e627737d4fb96d635419e2a15cb09f5c2d6d Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Tue, 15 Jun 2021 16:55:19 +0200 Subject: [PATCH 23/64] Rename stylesheet Signed-off-by: Emanuele Feliziani --- src/UI/CredentialsAutofill.js | 2 +- src/UI/EmailAutofill.js | 2 +- .../styles/{DDGAutofill-styles.js => email-autofill-styles.js} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/UI/styles/{DDGAutofill-styles.js => email-autofill-styles.js} (100%) diff --git a/src/UI/CredentialsAutofill.js b/src/UI/CredentialsAutofill.js index e3575ff48..6ebd2dc84 100644 --- a/src/UI/CredentialsAutofill.js +++ b/src/UI/CredentialsAutofill.js @@ -12,7 +12,7 @@ class CredentialsAutofill extends Tooltip { this.credentials = this.interface.getLocalCredentials() const includeStyles = isApp - ? `` + ? `` : `` this.shadow.innerHTML = ` diff --git a/src/UI/EmailAutofill.js b/src/UI/EmailAutofill.js index cbb6a9a19..ee8810977 100644 --- a/src/UI/EmailAutofill.js +++ b/src/UI/EmailAutofill.js @@ -13,7 +13,7 @@ class EmailAutofill extends Tooltip { this.addresses = this.interface.getLocalAddresses() const includeStyles = isApp - ? `` + ? `` : `` this.shadow.innerHTML = ` diff --git a/src/UI/styles/DDGAutofill-styles.js b/src/UI/styles/email-autofill-styles.js similarity index 100% rename from src/UI/styles/DDGAutofill-styles.js rename to src/UI/styles/email-autofill-styles.js From 1212f2ce1f4cada51b90aec7bf3003b8c9c495f2 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Tue, 15 Jun 2021 16:55:51 +0200 Subject: [PATCH 24/64] Create credentials-specific styles Signed-off-by: Emanuele Feliziani --- src/UI/CredentialsAutofill.js | 3 +- src/UI/img/ddg-password-icon-base-white.svg | 10 +++ src/UI/img/ddg-password-icon-base.svg | 10 +++ src/UI/img/ddg-password-icon-filled.svg | 10 +++ src/UI/img/ddg-password-icon-focused.svg | 13 ++++ src/UI/img/ddgPasswordIcon.js | 6 ++ src/UI/styles/credentials-autofill-styles.js | 81 ++++++++++++++++++++ 7 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 src/UI/img/ddg-password-icon-base-white.svg create mode 100644 src/UI/img/ddg-password-icon-base.svg create mode 100644 src/UI/img/ddg-password-icon-filled.svg create mode 100644 src/UI/img/ddg-password-icon-focused.svg create mode 100644 src/UI/img/ddgPasswordIcon.js create mode 100644 src/UI/styles/credentials-autofill-styles.js diff --git a/src/UI/CredentialsAutofill.js b/src/UI/CredentialsAutofill.js index 6ebd2dc84..c76c319a7 100644 --- a/src/UI/CredentialsAutofill.js +++ b/src/UI/CredentialsAutofill.js @@ -21,7 +21,8 @@ ${includeStyles} diff --git a/src/UI/img/ddg-password-icon-base-white.svg b/src/UI/img/ddg-password-icon-base-white.svg new file mode 100644 index 000000000..09580249a --- /dev/null +++ b/src/UI/img/ddg-password-icon-base-white.svg @@ -0,0 +1,10 @@ + + + ddg-password-icon-base-white + + + + + + + \ No newline at end of file diff --git a/src/UI/img/ddg-password-icon-base.svg b/src/UI/img/ddg-password-icon-base.svg new file mode 100644 index 000000000..bd7458d57 --- /dev/null +++ b/src/UI/img/ddg-password-icon-base.svg @@ -0,0 +1,10 @@ + + + ddg-password-icon-base + + + + + + + \ No newline at end of file diff --git a/src/UI/img/ddg-password-icon-filled.svg b/src/UI/img/ddg-password-icon-filled.svg new file mode 100644 index 000000000..c2ad30f06 --- /dev/null +++ b/src/UI/img/ddg-password-icon-filled.svg @@ -0,0 +1,10 @@ + + + ddg-password-icon-filled + + + + + + + \ No newline at end of file diff --git a/src/UI/img/ddg-password-icon-focused.svg b/src/UI/img/ddg-password-icon-focused.svg new file mode 100644 index 000000000..ac81c9b93 --- /dev/null +++ b/src/UI/img/ddg-password-icon-focused.svg @@ -0,0 +1,13 @@ + + + ddg-password-icon-focused + + + + + + + + + + \ No newline at end of file diff --git a/src/UI/img/ddgPasswordIcon.js b/src/UI/img/ddgPasswordIcon.js new file mode 100644 index 000000000..26c3143f5 --- /dev/null +++ b/src/UI/img/ddgPasswordIcon.js @@ -0,0 +1,6 @@ +const ddgPasswordIconBase = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+ZGRnLXBhc3N3b3JkLWljb24tYmFzZTwvdGl0bGU+CiAgICA8ZyBpZD0iZGRnLXBhc3N3b3JkLWljb24tYmFzZSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IlVuaW9uIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0LjAwMDAwMCwgNC4wMDAwMDApIiBmaWxsPSIjMDAwMDAwIj4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzMzMsMi42NjY2NyBDMTAuMjI4OCwyLjY2NjY3IDkuMzMzMzMsMy41NjIxIDkuMzMzMzMsNC42NjY2NyBDOS4zMzMzMyw1Ljc3MTI0IDEwLjIyODgsNi42NjY2NyAxMS4zMzMzLDYuNjY2NjcgQzEyLjQzNzksNi42NjY2NyAxMy4zMzMzLDUuNzcxMjQgMTMuMzMzMyw0LjY2NjY3IEMxMy4zMzMzLDMuNTYyMSAxMi40Mzc5LDIuNjY2NjcgMTEuMzMzMywyLjY2NjY3IFogTTEwLjY2NjcsNC42NjY2NyBDMTAuNjY2Nyw0LjI5ODQ4IDEwLjk2NTEsNCAxMS4zMzMzLDQgQzExLjcwMTUsNCAxMiw0LjI5ODQ4IDEyLDQuNjY2NjcgQzEyLDUuMDM0ODYgMTEuNzAxNSw1LjMzMzMzIDExLjMzMzMsNS4zMzMzMyBDMTAuOTY1MSw1LjMzMzMzIDEwLjY2NjcsNS4wMzQ4NiAxMC42NjY3LDQuNjY2NjcgWiIgaWQ9IlNoYXBlIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMC42NjY3LDAgQzcuNzIxMTUsMCA1LjMzMzMzLDIuMzg3ODEgNS4zMzMzMyw1LjMzMzMzIEM1LjMzMzMzLDUuNzYxMTkgNS4zODM4NSw2LjE3Nzk4IDUuNDc5NDUsNi41Nzc3NSBMMC4xOTUyNjIsMTEuODYxOSBDMC4wNzAyMzc5LDExLjk4NyAwLDEyLjE1NjUgMCwxMi4zMzMzIEwwLDE1LjMzMzMgQzAsMTUuNzAxNSAwLjI5ODQ3NywxNiAwLjY2NjY2NywxNiBMMy4zMzMzMywxNiBDNC4wNjk3MSwxNiA0LjY2NjY3LDE1LjQwMyA0LjY2NjY3LDE0LjY2NjcgTDQuNjY2NjcsMTQgTDUuMzMzMzMsMTQgQzYuMDY5NzEsMTQgNi42NjY2NywxMy40MDMgNi42NjY2NywxMi42NjY3IEw2LjY2NjY3LDExLjMzMzMgTDgsMTEuMzMzMyBDOC4xNzY4MSwxMS4zMzMzIDguMzQ2MzgsMTEuMjYzMSA4LjQ3MTQxLDExLjEzODEgTDkuMTU5MDYsMTAuNDUwNCBDOS42Mzc3MiwxMC41OTEyIDEwLjE0MzksMTAuNjY2NyAxMC42NjY3LDEwLjY2NjcgQzEzLjYxMjIsMTAuNjY2NyAxNiw4LjI3ODg1IDE2LDUuMzMzMzMgQzE2LDIuMzg3ODEgMTMuNjEyMiwwIDEwLjY2NjcsMCBaIE02LjY2NjY3LDUuMzMzMzMgQzYuNjY2NjcsMy4xMjQxOSA4LjQ1NzUzLDEuMzMzMzMgMTAuNjY2NywxLjMzMzMzIEMxMi44NzU4LDEuMzMzMzMgMTQuNjY2NywzLjEyNDE5IDE0LjY2NjcsNS4zMzMzMyBDMTQuNjY2Nyw3LjU0MjQ3IDEyLjg3NTgsOS4zMzMzMyAxMC42NjY3LDkuMzMzMzMgQzEwLjE1NTgsOS4zMzMzMyA5LjY2ODg2LDkuMjM3OSA5LjIyMTUyLDkuMDY0NSBDOC45NzUyOCw4Ljk2OTA1IDguNjk1OTEsOS4wMjc5NSA4LjUwOTE2LDkuMjE0NjkgTDcuNzIzODYsMTAgTDYsMTAgQzUuNjMxODEsMTAgNS4zMzMzMywxMC4yOTg1IDUuMzMzMzMsMTAuNjY2NyBMNS4zMzMzMywxMi42NjY3IEw0LDEyLjY2NjcgQzMuNjMxODEsMTIuNjY2NyAzLjMzMzMzLDEyLjk2NTEgMy4zMzMzMywxMy4zMzMzIEwzLjMzMzMzLDE0LjY2NjcgTDEuMzMzMzMsMTQuNjY2NyBMMS4zMzMzMywxMi42MDk1IEw2LjY5Nzg3LDcuMjQ0OTQgQzYuODc1MDIsNy4wNjc3OSA2LjkzNzksNi44MDYyOSA2Ljg2MDY1LDYuNTY3OTggQzYuNzM0ODksNi4xNzk5NyA2LjY2NjY3LDUuNzY1MjcgNi42NjY2Nyw1LjMzMzMzIFoiIGlkPSJTaGFwZSI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+' +const ddgPasswordIconBaseWhite = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+ZGRnLXBhc3N3b3JkLWljb24tYmFzZS13aGl0ZTwvdGl0bGU+CiAgICA8ZyBpZD0iZGRnLXBhc3N3b3JkLWljb24tYmFzZS13aGl0ZSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IlVuaW9uIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0LjAwMDAwMCwgNC4wMDAwMDApIiBmaWxsPSIjRkZGRkZGIj4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzMzMsMi42NjY2NyBDMTAuMjI4OCwyLjY2NjY3IDkuMzMzMzMsMy41NjIxIDkuMzMzMzMsNC42NjY2NyBDOS4zMzMzMyw1Ljc3MTI0IDEwLjIyODgsNi42NjY2NyAxMS4zMzMzLDYuNjY2NjcgQzEyLjQzNzksNi42NjY2NyAxMy4zMzMzLDUuNzcxMjQgMTMuMzMzMyw0LjY2NjY3IEMxMy4zMzMzLDMuNTYyMSAxMi40Mzc5LDIuNjY2NjcgMTEuMzMzMywyLjY2NjY3IFogTTEwLjY2NjcsNC42NjY2NyBDMTAuNjY2Nyw0LjI5ODQ4IDEwLjk2NTEsNCAxMS4zMzMzLDQgQzExLjcwMTUsNCAxMiw0LjI5ODQ4IDEyLDQuNjY2NjcgQzEyLDUuMDM0ODYgMTEuNzAxNSw1LjMzMzMzIDExLjMzMzMsNS4zMzMzMyBDMTAuOTY1MSw1LjMzMzMzIDEwLjY2NjcsNS4wMzQ4NiAxMC42NjY3LDQuNjY2NjcgWiIgaWQ9IlNoYXBlIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMC42NjY3LDAgQzcuNzIxMTUsMCA1LjMzMzMzLDIuMzg3ODEgNS4zMzMzMyw1LjMzMzMzIEM1LjMzMzMzLDUuNzYxMTkgNS4zODM4NSw2LjE3Nzk4IDUuNDc5NDUsNi41Nzc3NSBMMC4xOTUyNjIsMTEuODYxOSBDMC4wNzAyMzc5LDExLjk4NyAwLDEyLjE1NjUgMCwxMi4zMzMzIEwwLDE1LjMzMzMgQzAsMTUuNzAxNSAwLjI5ODQ3NywxNiAwLjY2NjY2NywxNiBMMy4zMzMzMywxNiBDNC4wNjk3MSwxNiA0LjY2NjY3LDE1LjQwMyA0LjY2NjY3LDE0LjY2NjcgTDQuNjY2NjcsMTQgTDUuMzMzMzMsMTQgQzYuMDY5NzEsMTQgNi42NjY2NywxMy40MDMgNi42NjY2NywxMi42NjY3IEw2LjY2NjY3LDExLjMzMzMgTDgsMTEuMzMzMyBDOC4xNzY4MSwxMS4zMzMzIDguMzQ2MzgsMTEuMjYzMSA4LjQ3MTQxLDExLjEzODEgTDkuMTU5MDYsMTAuNDUwNCBDOS42Mzc3MiwxMC41OTEyIDEwLjE0MzksMTAuNjY2NyAxMC42NjY3LDEwLjY2NjcgQzEzLjYxMjIsMTAuNjY2NyAxNiw4LjI3ODg1IDE2LDUuMzMzMzMgQzE2LDIuMzg3ODEgMTMuNjEyMiwwIDEwLjY2NjcsMCBaIE02LjY2NjY3LDUuMzMzMzMgQzYuNjY2NjcsMy4xMjQxOSA4LjQ1NzUzLDEuMzMzMzMgMTAuNjY2NywxLjMzMzMzIEMxMi44NzU4LDEuMzMzMzMgMTQuNjY2NywzLjEyNDE5IDE0LjY2NjcsNS4zMzMzMyBDMTQuNjY2Nyw3LjU0MjQ3IDEyLjg3NTgsOS4zMzMzMyAxMC42NjY3LDkuMzMzMzMgQzEwLjE1NTgsOS4zMzMzMyA5LjY2ODg2LDkuMjM3OSA5LjIyMTUyLDkuMDY0NSBDOC45NzUyOCw4Ljk2OTA1IDguNjk1OTEsOS4wMjc5NSA4LjUwOTE2LDkuMjE0NjkgTDcuNzIzODYsMTAgTDYsMTAgQzUuNjMxODEsMTAgNS4zMzMzMywxMC4yOTg1IDUuMzMzMzMsMTAuNjY2NyBMNS4zMzMzMywxMi42NjY3IEw0LDEyLjY2NjcgQzMuNjMxODEsMTIuNjY2NyAzLjMzMzMzLDEyLjk2NTEgMy4zMzMzMywxMy4zMzMzIEwzLjMzMzMzLDE0LjY2NjcgTDEuMzMzMzMsMTQuNjY2NyBMMS4zMzMzMywxMi42MDk1IEw2LjY5Nzg3LDcuMjQ0OTQgQzYuODc1MDIsNy4wNjc3OSA2LjkzNzksNi44MDYyOSA2Ljg2MDY1LDYuNTY3OTggQzYuNzM0ODksNi4xNzk5NyA2LjY2NjY3LDUuNzY1MjcgNi42NjY2Nyw1LjMzMzMzIFoiIGlkPSJTaGFwZSI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+' +const ddgPasswordIconFilled = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+ZGRnLXBhc3N3b3JkLWljb24tZmlsbGVkPC90aXRsZT4KICAgIDxnIGlkPSJkZGctcGFzc3dvcmQtaWNvbi1maWxsZWQiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJTaGFwZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNC4wMDAwMDAsIDQuMDAwMDAwKSIgZmlsbD0iIzc2NDMxMCI+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMS4yNSwyLjc1IEMxMC4xNDU0LDIuNzUgOS4yNSwzLjY0NTQzIDkuMjUsNC43NSBDOS4yNSw1Ljg1NDU3IDEwLjE0NTQsNi43NSAxMS4yNSw2Ljc1IEMxMi4zNTQ2LDYuNzUgMTMuMjUsNS44NTQ1NyAxMy4yNSw0Ljc1IEMxMy4yNSwzLjY0NTQzIDEyLjM1NDYsMi43NSAxMS4yNSwyLjc1IFogTTEwLjc1LDQuNzUgQzEwLjc1LDQuNDczODYgMTAuOTczOSw0LjI1IDExLjI1LDQuMjUgQzExLjUyNjEsNC4yNSAxMS43NSw0LjQ3Mzg2IDExLjc1LDQuNzUgQzExLjc1LDUuMDI2MTQgMTEuNTI2MSw1LjI1IDExLjI1LDUuMjUgQzEwLjk3MzksNS4yNSAxMC43NSw1LjAyNjE0IDEwLjc1LDQuNzUgWiI+PC9wYXRoPgogICAgICAgICAgICA8cGF0aCBkPSJNMTAuNjI1LDAgQzcuNjU2NDcsMCA1LjI1LDIuNDA2NDcgNS4yNSw1LjM3NSBDNS4yNSw1Ljc4MDk4IDUuMjk1MTQsNi4xNzcxNCA1LjM4MDg4LDYuNTU4NDYgTDAuMjE5NjcsMTEuNzE5NyBDMC4wNzkwMTc2LDExLjg2MDMgMCwxMi4wNTExIDAsMTIuMjUgTDAsMTUuMjUgQzAsMTUuNjY0MiAwLjMzNTc4NiwxNiAwLjc1LDE2IEwzLjc0NjYxLDE2IEM0LjMwMDc2LDE2IDQuNzUsMTUuNTUwOCA0Ljc1LDE0Ljk5NjYgTDQuNzUsMTQgTDUuNzQ2NjEsMTQgQzYuMzAwNzYsMTQgNi43NSwxMy41NTA4IDYuNzUsMTIuOTk2NiBMNi43NSwxMS41IEw4LDExLjUgQzguMTk4OTEsMTEuNSA4LjM4OTY4LDExLjQyMSA4LjUzMDMzLDExLjI4MDMgTDkuMjQwNzgsMTAuNTY5OSBDOS42ODMwNCwxMC42ODc1IDEwLjE0NzIsMTAuNzUgMTAuNjI1LDEwLjc1IEMxMy41OTM1LDEwLjc1IDE2LDguMzQzNTMgMTYsNS4zNzUgQzE2LDIuNDA2NDcgMTMuNTkzNSwwIDEwLjYyNSwwIFogTTYuNzUsNS4zNzUgQzYuNzUsMy4yMzQ5IDguNDg0OSwxLjUgMTAuNjI1LDEuNSBDMTIuNzY1MSwxLjUgMTQuNSwzLjIzNDkgMTQuNSw1LjM3NSBDMTQuNSw3LjUxNTEgMTIuNzY1MSw5LjI1IDEwLjYyNSw5LjI1IEMxMC4xNTQ1LDkuMjUgOS43MDUyOCw5LjE2NjUgOS4yOTAxMSw5LjAxNDE2IEM5LjAxNTgxLDguOTEzNSA4LjcwODAzLDguOTgxMzEgOC41MDE0Miw5LjE4NzkyIEw3LjY4OTM0LDEwIEw2LDEwIEM1LjU4NTc5LDEwIDUuMjUsMTAuMzM1OCA1LjI1LDEwLjc1IEw1LjI1LDEyLjUgTDQsMTIuNSBDMy41ODU3OSwxMi41IDMuMjUsMTIuODM1OCAzLjI1LDEzLjI1IEwzLjI1LDE0LjUgTDEuNSwxNC41IEwxLjUsMTIuNTYwNyBMNi43NDgyNiw3LjMxMjQgQzYuOTQ2NjYsNy4xMTQgNy4wMTc3Myw2LjgyMTQ1IDYuOTMyNDUsNi41NTQxMyBDNi44MTQxNSw2LjE4MzI3IDYuNzUsNS43ODczNSA2Ljc1LDUuMzc1IFoiPjwvcGF0aD4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==' +const ddgPasswordIconFocused = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+ZGRnLXBhc3N3b3JkLWljb24tZm9jdXNlZDwvdGl0bGU+CiAgICA8ZyBpZD0iZGRnLXBhc3N3b3JkLWljb24tZm9jdXNlZCIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9Ikljb24tQ29udGFpbmVyIiBmaWxsPSIjMDAwMDAwIj4KICAgICAgICAgICAgPHJlY3QgaWQ9IlJlY3RhbmdsZSIgZmlsbC1vcGFjaXR5PSIwLjEiIGZpbGwtcnVsZT0ibm9uemVybyIgeD0iMCIgeT0iMCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iMTIiPjwvcmVjdD4KICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0LjAwMDAwMCwgNC4wMDAwMDApIiBmaWxsLW9wYWNpdHk9IjAuOSI+CiAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEuMjUsMi43NSBDMTAuMTQ1NCwyLjc1IDkuMjUsMy42NDU0MyA5LjI1LDQuNzUgQzkuMjUsNS44NTQ1NyAxMC4xNDU0LDYuNzUgMTEuMjUsNi43NSBDMTIuMzU0Niw2Ljc1IDEzLjI1LDUuODU0NTcgMTMuMjUsNC43NSBDMTMuMjUsMy42NDU0MyAxMi4zNTQ2LDIuNzUgMTEuMjUsMi43NSBaIE0xMC43NSw0Ljc1IEMxMC43NSw0LjQ3Mzg2IDEwLjk3MzksNC4yNSAxMS4yNSw0LjI1IEMxMS41MjYxLDQuMjUgMTEuNzUsNC40NzM4NiAxMS43NSw0Ljc1IEMxMS43NSw1LjAyNjE0IDExLjUyNjEsNS4yNSAxMS4yNSw1LjI1IEMxMC45NzM5LDUuMjUgMTAuNzUsNS4wMjYxNCAxMC43NSw0Ljc1IFoiIGlkPSJTaGFwZSI+PC9wYXRoPgogICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLjYyNSwwIEM3LjY1NjUsMCA1LjI1LDIuNDA2NDcgNS4yNSw1LjM3NSBDNS4yNSw1Ljc4MDk4IDUuMjk1MTQsNi4xNzcxIDUuMzgwODgsNi41NTg1IEwwLjIxOTY3LDExLjcxOTcgQzAuMDc5MDIsMTEuODYwMyAwLDEyLjA1MTEgMCwxMi4yNSBMMCwxNS4yNSBDMCwxNS42NjQyIDAuMzM1NzksMTYgMC43NSwxNiBMMy43NDY2MSwxNiBDNC4zMDA3NiwxNiA0Ljc1LDE1LjU1MDggNC43NSwxNC45OTY2IEw0Ljc1LDE0IEw1Ljc0NjYxLDE0IEM2LjMwMDgsMTQgNi43NSwxMy41NTA4IDYuNzUsMTIuOTk2NiBMNi43NSwxMS41IEw4LDExLjUgQzguMTk4OSwxMS41IDguMzg5NywxMS40MjEgOC41MzAzLDExLjI4MDMgTDkuMjQwOCwxMC41Njk5IEM5LjY4MywxMC42ODc1IDEwLjE0NzIsMTAuNzUgMTAuNjI1LDEwLjc1IEMxMy41OTM1LDEwLjc1IDE2LDguMzQzNSAxNiw1LjM3NSBDMTYsMi40MDY0NyAxMy41OTM1LDAgMTAuNjI1LDAgWiBNNi43NSw1LjM3NSBDNi43NSwzLjIzNDkgOC40ODQ5LDEuNSAxMC42MjUsMS41IEMxMi43NjUxLDEuNSAxNC41LDMuMjM0OSAxNC41LDUuMzc1IEMxNC41LDcuNTE1MSAxMi43NjUxLDkuMjUgMTAuNjI1LDkuMjUgQzEwLjE1NDUsOS4yNSA5LjcwNTMsOS4xNjY1IDkuMjkwMSw5LjAxNDIgQzkuMDE1OCw4LjkxMzUgOC43MDgsOC45ODEzIDguNTAxNCw5LjE4NzkgTDcuNjg5MywxMCBMNiwxMCBDNS41ODU3OSwxMCA1LjI1LDEwLjMzNTggNS4yNSwxMC43NSBMNS4yNSwxMi41IEw0LDEyLjUgQzMuNTg1NzksMTIuNSAzLjI1LDEyLjgzNTggMy4yNSwxMy4yNSBMMy4yNSwxNC41IEwxLjUsMTQuNSBMMS41LDEyLjU2MDcgTDYuNzQ4Myw3LjMxMjQgQzYuOTQ2Nyw3LjExNCA3LjAxNzcsNi44MjE0IDYuOTMyNSw2LjU1NDEgQzYuODE0MSw2LjE4MzMgNi43NSw1Ljc4NzM1IDYuNzUsNS4zNzUgWiIgaWQ9IlNoYXBlIj48L3BhdGg+CiAgICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==' + +module.exports = {ddgPasswordIconBase, ddgPasswordIconBaseWhite, ddgPasswordIconFilled, ddgPasswordIconFocused} diff --git a/src/UI/styles/credentials-autofill-styles.js b/src/UI/styles/credentials-autofill-styles.js new file mode 100644 index 000000000..a15affc3a --- /dev/null +++ b/src/UI/styles/credentials-autofill-styles.js @@ -0,0 +1,81 @@ +module.exports = ` +.wrapper *, .wrapper *::before, .wrapper *::after { + box-sizing: border-box; +} +.wrapper { + position: fixed; + top: 0; + left: 0; + padding: 0; + font-family: 'SF Pro Text', -apple-system, + BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', + 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + /* move it offscreen to avoid flashing */ + transform: translate(-1000px); + z-index: 2147483647; +} +.tooltip { + position: absolute; + top: 100%; + left: 100%; + width: 280px; + max-width: calc(100vw - 25px); + padding: 6px; + border: 0.5px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + background-color: rgba(242, 240, 240, 0.9); + -webkit-backdrop-filter: blur(40px); + backdrop-filter: blur(40px); + font-size: 13px; + line-height: 15px; + color: #222222; + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.32); + z-index: 2147483647; +} +.tooltip__button { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + width: 100%; + padding: 4px 8px 7px; + font-family: inherit; + font-size: inherit; + font-weight: 500; + text-align: left; + letter-spacing: -0.25px; + background: transparent; + border: none; + border-radius: 6px; +} +.tooltip__button:first-child { + margin-top: 0; +} +.tooltip__button:last-child { + margin-bottom: 0; +} +.tooltip__button::before { + content: ''; + display: block; + width: 36px; + height: 36px; + margin: auto 6px auto 0; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+ZGRnLXBhc3N3b3JkLWljb24tYmFzZTwvdGl0bGU+CiAgICA8ZyBpZD0iZGRnLXBhc3N3b3JkLWljb24tYmFzZSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IlVuaW9uIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0LjAwMDAwMCwgNC4wMDAwMDApIiBmaWxsPSIjMDAwMDAwIj4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzMzMsMi42NjY2NyBDMTAuMjI4OCwyLjY2NjY3IDkuMzMzMzMsMy41NjIxIDkuMzMzMzMsNC42NjY2NyBDOS4zMzMzMyw1Ljc3MTI0IDEwLjIyODgsNi42NjY2NyAxMS4zMzMzLDYuNjY2NjcgQzEyLjQzNzksNi42NjY2NyAxMy4zMzMzLDUuNzcxMjQgMTMuMzMzMyw0LjY2NjY3IEMxMy4zMzMzLDMuNTYyMSAxMi40Mzc5LDIuNjY2NjcgMTEuMzMzMywyLjY2NjY3IFogTTEwLjY2NjcsNC42NjY2NyBDMTAuNjY2Nyw0LjI5ODQ4IDEwLjk2NTEsNCAxMS4zMzMzLDQgQzExLjcwMTUsNCAxMiw0LjI5ODQ4IDEyLDQuNjY2NjcgQzEyLDUuMDM0ODYgMTEuNzAxNSw1LjMzMzMzIDExLjMzMzMsNS4zMzMzMyBDMTAuOTY1MSw1LjMzMzMzIDEwLjY2NjcsNS4wMzQ4NiAxMC42NjY3LDQuNjY2NjcgWiIgaWQ9IlNoYXBlIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMC42NjY3LDAgQzcuNzIxMTUsMCA1LjMzMzMzLDIuMzg3ODEgNS4zMzMzMyw1LjMzMzMzIEM1LjMzMzMzLDUuNzYxMTkgNS4zODM4NSw2LjE3Nzk4IDUuNDc5NDUsNi41Nzc3NSBMMC4xOTUyNjIsMTEuODYxOSBDMC4wNzAyMzc5LDExLjk4NyAwLDEyLjE1NjUgMCwxMi4zMzMzIEwwLDE1LjMzMzMgQzAsMTUuNzAxNSAwLjI5ODQ3NywxNiAwLjY2NjY2NywxNiBMMy4zMzMzMywxNiBDNC4wNjk3MSwxNiA0LjY2NjY3LDE1LjQwMyA0LjY2NjY3LDE0LjY2NjcgTDQuNjY2NjcsMTQgTDUuMzMzMzMsMTQgQzYuMDY5NzEsMTQgNi42NjY2NywxMy40MDMgNi42NjY2NywxMi42NjY3IEw2LjY2NjY3LDExLjMzMzMgTDgsMTEuMzMzMyBDOC4xNzY4MSwxMS4zMzMzIDguMzQ2MzgsMTEuMjYzMSA4LjQ3MTQxLDExLjEzODEgTDkuMTU5MDYsMTAuNDUwNCBDOS42Mzc3MiwxMC41OTEyIDEwLjE0MzksMTAuNjY2NyAxMC42NjY3LDEwLjY2NjcgQzEzLjYxMjIsMTAuNjY2NyAxNiw4LjI3ODg1IDE2LDUuMzMzMzMgQzE2LDIuMzg3ODEgMTMuNjEyMiwwIDEwLjY2NjcsMCBaIE02LjY2NjY3LDUuMzMzMzMgQzYuNjY2NjcsMy4xMjQxOSA4LjQ1NzUzLDEuMzMzMzMgMTAuNjY2NywxLjMzMzMzIEMxMi44NzU4LDEuMzMzMzMgMTQuNjY2NywzLjEyNDE5IDE0LjY2NjcsNS4zMzMzMyBDMTQuNjY2Nyw3LjU0MjQ3IDEyLjg3NTgsOS4zMzMzMyAxMC42NjY3LDkuMzMzMzMgQzEwLjE1NTgsOS4zMzMzMyA5LjY2ODg2LDkuMjM3OSA5LjIyMTUyLDkuMDY0NSBDOC45NzUyOCw4Ljk2OTA1IDguNjk1OTEsOS4wMjc5NSA4LjUwOTE2LDkuMjE0NjkgTDcuNzIzODYsMTAgTDYsMTAgQzUuNjMxODEsMTAgNS4zMzMzMywxMC4yOTg1IDUuMzMzMzMsMTAuNjY2NyBMNS4zMzMzMywxMi42NjY3IEw0LDEyLjY2NjcgQzMuNjMxODEsMTIuNjY2NyAzLjMzMzMzLDEyLjk2NTEgMy4zMzMzMywxMy4zMzMzIEwzLjMzMzMzLDE0LjY2NjcgTDEuMzMzMzMsMTQuNjY2NyBMMS4zMzMzMywxMi42MDk1IEw2LjY5Nzg3LDcuMjQ0OTQgQzYuODc1MDIsNy4wNjc3OSA2LjkzNzksNi44MDYyOSA2Ljg2MDY1LDYuNTY3OTggQzYuNzM0ODksNi4xNzk5NyA2LjY2NjY3LDUuNzY1MjcgNi42NjY2Nyw1LjMzMzMzIFoiIGlkPSJTaGFwZSI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'); + background-size: cover; +} +.tooltip__button:hover { + background-color: #3969EF; + color: #FFFFFF; +} +.tooltip__button:hover::before { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+ZGRnLXBhc3N3b3JkLWljb24tYmFzZS13aGl0ZTwvdGl0bGU+CiAgICA8ZyBpZD0iZGRnLXBhc3N3b3JkLWljb24tYmFzZS13aGl0ZSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IlVuaW9uIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0LjAwMDAwMCwgNC4wMDAwMDApIiBmaWxsPSIjRkZGRkZGIj4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzMzMsMi42NjY2NyBDMTAuMjI4OCwyLjY2NjY3IDkuMzMzMzMsMy41NjIxIDkuMzMzMzMsNC42NjY2NyBDOS4zMzMzMyw1Ljc3MTI0IDEwLjIyODgsNi42NjY2NyAxMS4zMzMzLDYuNjY2NjcgQzEyLjQzNzksNi42NjY2NyAxMy4zMzMzLDUuNzcxMjQgMTMuMzMzMyw0LjY2NjY3IEMxMy4zMzMzLDMuNTYyMSAxMi40Mzc5LDIuNjY2NjcgMTEuMzMzMywyLjY2NjY3IFogTTEwLjY2NjcsNC42NjY2NyBDMTAuNjY2Nyw0LjI5ODQ4IDEwLjk2NTEsNCAxMS4zMzMzLDQgQzExLjcwMTUsNCAxMiw0LjI5ODQ4IDEyLDQuNjY2NjcgQzEyLDUuMDM0ODYgMTEuNzAxNSw1LjMzMzMzIDExLjMzMzMsNS4zMzMzMyBDMTAuOTY1MSw1LjMzMzMzIDEwLjY2NjcsNS4wMzQ4NiAxMC42NjY3LDQuNjY2NjcgWiIgaWQ9IlNoYXBlIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMC42NjY3LDAgQzcuNzIxMTUsMCA1LjMzMzMzLDIuMzg3ODEgNS4zMzMzMyw1LjMzMzMzIEM1LjMzMzMzLDUuNzYxMTkgNS4zODM4NSw2LjE3Nzk4IDUuNDc5NDUsNi41Nzc3NSBMMC4xOTUyNjIsMTEuODYxOSBDMC4wNzAyMzc5LDExLjk4NyAwLDEyLjE1NjUgMCwxMi4zMzMzIEwwLDE1LjMzMzMgQzAsMTUuNzAxNSAwLjI5ODQ3NywxNiAwLjY2NjY2NywxNiBMMy4zMzMzMywxNiBDNC4wNjk3MSwxNiA0LjY2NjY3LDE1LjQwMyA0LjY2NjY3LDE0LjY2NjcgTDQuNjY2NjcsMTQgTDUuMzMzMzMsMTQgQzYuMDY5NzEsMTQgNi42NjY2NywxMy40MDMgNi42NjY2NywxMi42NjY3IEw2LjY2NjY3LDExLjMzMzMgTDgsMTEuMzMzMyBDOC4xNzY4MSwxMS4zMzMzIDguMzQ2MzgsMTEuMjYzMSA4LjQ3MTQxLDExLjEzODEgTDkuMTU5MDYsMTAuNDUwNCBDOS42Mzc3MiwxMC41OTEyIDEwLjE0MzksMTAuNjY2NyAxMC42NjY3LDEwLjY2NjcgQzEzLjYxMjIsMTAuNjY2NyAxNiw4LjI3ODg1IDE2LDUuMzMzMzMgQzE2LDIuMzg3ODEgMTMuNjEyMiwwIDEwLjY2NjcsMCBaIE02LjY2NjY3LDUuMzMzMzMgQzYuNjY2NjcsMy4xMjQxOSA4LjQ1NzUzLDEuMzMzMzMgMTAuNjY2NywxLjMzMzMzIEMxMi44NzU4LDEuMzMzMzMgMTQuNjY2NywzLjEyNDE5IDE0LjY2NjcsNS4zMzMzMyBDMTQuNjY2Nyw3LjU0MjQ3IDEyLjg3NTgsOS4zMzMzMyAxMC42NjY3LDkuMzMzMzMgQzEwLjE1NTgsOS4zMzMzMyA5LjY2ODg2LDkuMjM3OSA5LjIyMTUyLDkuMDY0NSBDOC45NzUyOCw4Ljk2OTA1IDguNjk1OTEsOS4wMjc5NSA4LjUwOTE2LDkuMjE0NjkgTDcuNzIzODYsMTAgTDYsMTAgQzUuNjMxODEsMTAgNS4zMzMzMywxMC4yOTg1IDUuMzMzMzMsMTAuNjY2NyBMNS4zMzMzMywxMi42NjY3IEw0LDEyLjY2NjcgQzMuNjMxODEsMTIuNjY2NyAzLjMzMzMzLDEyLjk2NTEgMy4zMzMzMywxMy4zMzMzIEwzLjMzMzMzLDE0LjY2NjcgTDEuMzMzMzMsMTQuNjY2NyBMMS4zMzMzMywxMi42MDk1IEw2LjY5Nzg3LDcuMjQ0OTQgQzYuODc1MDIsNy4wNjc3OSA2LjkzNzksNi44MDYyOSA2Ljg2MDY1LDYuNTY3OTggQzYuNzM0ODksNi4xNzk5NyA2LjY2NjY3LDUuNzY1MjcgNi42NjY2Nyw1LjMzMzMzIFoiIGlkPSJTaGFwZSI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'); +} +.tooltip__button__password { + font-size: 11px; + color: rgba(0,0,0,0.6); +} +.tooltip__button:hover .tooltip__button__password { + color: #FFFFFF; +} +` From 2d25a1efa14c40671a05d1a67fb127f341e3ebbf Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Tue, 15 Jun 2021 16:57:07 +0200 Subject: [PATCH 25/64] Apply styles, icons, positioning Signed-off-by: Emanuele Feliziani --- src/DeviceInterface.js | 3 +-- src/Form/Form.js | 43 +++++++++++++++++++++++++++++++++--------- src/UI/Tooltip.js | 10 +++------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/DeviceInterface.js b/src/DeviceInterface.js index 05c81e3a9..a9abe2b49 100644 --- a/src/DeviceInterface.js +++ b/src/DeviceInterface.js @@ -1,6 +1,5 @@ const EmailAutofill = require('./UI/EmailAutofill') const CredentialsAutofill = require('./UI/CredentialsAutofill') -const {PASSWORD_SELECTOR} = require('./Form/selectors') const { isApp, notifyWebApp, @@ -30,7 +29,7 @@ const attachTooltip = function (form, input) { if (form.tooltip) return form.activeInput = input - form.tooltip = input.matches(PASSWORD_SELECTOR) + form.tooltip = form.isLogin ? new CredentialsAutofill(input, form, this) : new EmailAutofill(input, form, this) form.intObs.observe(input) diff --git a/src/Form/Form.js b/src/Form/Form.js index e5f012c86..36e254118 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -2,12 +2,15 @@ const FormAnalyzer = require('./FormAnalyzer') const {PASSWORD_SELECTOR} = require('./selectors') const {addInlineStyles, removeInlineStyles, isDDGApp, isApp, setValue, isEventWithinDax} = require('../autofill-utils') const {daxBase64} = require('./logo-svg') +const ddgPasswordIcons = require('../UI/img/ddgPasswordIcon') +const {EMAIL_SELECTOR} = require('./selectors') // In Firefox web_accessible_resources could leak a unique user identifier, so we avoid it here const isFirefox = navigator.userAgent.includes('Firefox') const getDaxImg = isDDGApp || isFirefox ? daxBase64 : chrome.runtime.getURL('img/logo-small.svg') +const getPasswordIcon = (variant = 'ddgPasswordIconBase') => ddgPasswordIcons[variant] -const getDaxStyles = input => ({ +const getDaxStyles = (input) => ({ // Height must be > 0 to account for fields initially hidden 'background-size': `auto ${input.offsetHeight <= 30 && input.offsetHeight > 0 ? '100%' : '26px'}`, 'background-position': 'center right', @@ -16,15 +19,31 @@ const getDaxStyles = input => ({ 'background-image': `url(${getDaxImg})` }) -const INLINE_AUTOFILLED_STYLES = { +const getPasswordStyles = (input) => ({ + ...getDaxStyles(input), + 'background-image': `url(${getPasswordIcon()})` +}) + +const getPasswordAutofilledStyles = (input) => ({ + ...getDaxStyles(input), + 'background-image': `url(${getPasswordIcon('ddgPasswordIconFilled')})`, 'background-color': '#F8F498', 'color': '#333333' -} +}) + +const getInlineAutofilledStyles = (input, isLogin) => isLogin + ? getPasswordAutofilledStyles(input) + : { + 'background-color': '#F8F498', + 'color': '#333333' + } class Form { constructor (form, input, DeviceInterface) { this.form = form this.formAnalyzer = new FormAnalyzer(form, input) + this.isLogin = this.formAnalyzer.isLogin + this.isSignup = this.formAnalyzer.isSignup this.Device = DeviceInterface this.attachTooltip = DeviceInterface.attachTooltip this.emailInputs = new Set() @@ -51,8 +70,9 @@ class Form { window.removeEventListener('mousedown', this.removeTooltip, {capture: true}) } this.removeInputHighlight = (input) => { - removeInlineStyles(input, INLINE_AUTOFILLED_STYLES) + removeInlineStyles(input, getInlineAutofilledStyles(input, this.isLogin)) input.classList.remove('ddg-autofilled') + this.addAutofillStyles(input) } this.removeAllHighlights = (e) => { // This ensures we are not removing the highlight ourselves when autofilling more than once @@ -92,12 +112,12 @@ class Form { } addInput (input) { - if (input.type === 'password') { + if (this.isLogin) { this.passwordInputs.add(input) - if (this.formAnalyzer.isLogin && this.Device.hasLocalCredentials) this.decorateInput(input) + if (this.Device.hasLocalCredentials) this.decorateInput(input) } else { this.emailInputs.add(input) - if (this.formAnalyzer.isSignup && this.Device.hasLocalAddresses) this.decorateInput(input) + if (this.Device.hasLocalAddresses && input.matches(EMAIL_SELECTOR)) this.decorateInput(input) } return this @@ -116,9 +136,14 @@ class Form { this.listeners.add({el, type, fn}) } + addAutofillStyles (input) { + const styles = this.isLogin ? getPasswordStyles(input) : getDaxStyles(input) + addInlineStyles(input, styles) + } + decorateInput (input) { input.setAttribute('data-ddg-autofill', 'true') - addInlineStyles(input, getDaxStyles(input)) + this.addAutofillStyles(input) this.addListener(input, 'mousemove', (e) => { if (isEventWithinDax(e, e.target)) { e.target.style.setProperty('cursor', 'pointer', 'important') @@ -150,7 +175,7 @@ class Form { autofillInput = (input, string) => { setValue(input, string) input.classList.add('ddg-autofilled') - addInlineStyles(input, INLINE_AUTOFILLED_STYLES) + addInlineStyles(input, getInlineAutofilledStyles(input, this.isLogin)) // If the user changes the alias, remove the decoration input.addEventListener('input', this.removeAllHighlights, {once: true}) diff --git a/src/UI/Tooltip.js b/src/UI/Tooltip.js index ee9b6cbeb..f47bc3db2 100644 --- a/src/UI/Tooltip.js +++ b/src/UI/Tooltip.js @@ -26,7 +26,9 @@ const checkPosition = function () { } this.animationFrame = window.requestAnimationFrame(() => { - const {left, bottom} = getDaxBoundingBox(this.input) + const {left, bottom} = this.associatedForm.isLogin + ? this.input.getBoundingClientRect() + : getDaxBoundingBox(this.input) if (left !== this.left || bottom !== this.top) { this.updatePosition({left, top: bottom}) @@ -109,12 +111,6 @@ class Tooltip { this.mutObs.observe(document.body, {childList: true, subtree: true, attributes: true}) window.addEventListener('scroll', this.checkPosition, {passive: true, capture: true}) } - setupStylesheet () { - this.stylesheet = this.shadow.querySelector('link, style') - // Un-hide once the style is loaded, to avoid flashing unstyled content - this.stylesheet.addEventListener('load', () => - this.tooltip.removeAttribute('hidden')) - } } module.exports = Tooltip From 1adcc2aff876abe76eacd46d9a2c9fc726a2ebe3 Mon Sep 17 00:00:00 2001 From: Emanuele Feliziani Date: Wed, 16 Jun 2021 14:24:02 +0200 Subject: [PATCH 26/64] Use a single stylesheet for both tooltips Signed-off-by: Emanuele Feliziani --- Gruntfile.js | 2 +- dist/autofill.css | 87 ++++++++++++++--- src/UI/CredentialsAutofill.js | 10 +- src/UI/EmailAutofill.js | 10 +- ...l-styles.js => autofill-tooltip-styles.js} | 97 +++++++++++++++---- src/UI/styles/email-autofill-styles.js | 77 --------------- 6 files changed, 164 insertions(+), 119 deletions(-) rename src/UI/styles/{credentials-autofill-styles.js => autofill-tooltip-styles.js} (82%) delete mode 100644 src/UI/styles/email-autofill-styles.js diff --git a/Gruntfile.js b/Gruntfile.js index 5fe614821..770d9fc5c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -27,7 +27,7 @@ module.exports = function (grunt) { target: 'src/**/*.js' }, exec: { - copyAutofillStylesToCSS: 'cp src/UI/styles/DDGAutofill-styles.js dist/autofill.css && sed -i "" \'/`/d\' dist/autofill.css', + copyAutofillStylesToCSS: 'cp src/UI/styles/autofill-tooltip-styles.js dist/autofill.css && sed -i "" \'/`/d\' dist/autofill.css', copyHostStyles: 'cp src/UI/styles/autofill-host-styles.css dist/autofill-host-styles_chrome.css && cp src/UI/styles/autofill-host-styles.css dist/autofill-host-styles_firefox.css', // Firefox and Chrome treat relative url differently in injected scripts. This fixes it. updateFirefoxRelativeUrl: `sed -i "" "s/chrome-extension:\\/\\/__MSG_@@extension_id__\\/public/../g" dist/autofill-host-styles_firefox.css` diff --git a/dist/autofill.css b/dist/autofill.css index 8323e130c..ed8b29222 100644 --- a/dist/autofill.css +++ b/dist/autofill.css @@ -14,24 +14,45 @@ transform: translate(-1000px); z-index: 2147483647; } +.wrapper--credentials { + font-family: 'SF Pro Text', -apple-system, + BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', + 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; +} .tooltip { position: absolute; - top: calc(100% + 6px); - right: calc(100% - 46px); width: 300px; max-width: calc(100vw - 25px); + z-index: 2147483647; +} +.tooltip--credentials { + top: 100%; + left: 100%; + padding: 6px; + border: 0.5px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + background-color: rgba(242, 240, 240, 0.9); + -webkit-backdrop-filter: blur(40px); + backdrop-filter: blur(40px); + font-size: 13px; + line-height: 15px; + color: #222222; + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.32); +} +.tooltip--email { + top: calc(100% + 6px); + right: calc(100% - 46px); padding: 8px; border: 1px solid #D0D0D0; border-radius: 10px; background-color: #FFFFFF; font-size: 14px; - color: #333333; line-height: 1.3; + color: #333333; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15); - z-index: 2147483647; } -.tooltip::before, -.tooltip::after { +.tooltip--email::before, +.tooltip--email::after { content: ""; width: 0; height: 0; @@ -42,23 +63,21 @@ position: absolute; right: 20px; } -.tooltip::before { +.tooltip--email::before { border-bottom-color: #D0D0D0; top: -9px; } -.tooltip::after { +.tooltip--email::after { border-bottom-color: #FFFFFF; top: -8px; } + +/* Buttons */ .tooltip__button { display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; width: 100%; padding: 4px 8px 7px; font-family: inherit; - font-size: 14px; background: transparent; border: none; border-radius: 6px; @@ -67,6 +86,50 @@ background-color: #3969EF; color: #FFFFFF; } + +/* Credentials tooltip specific */ +.tooltip__button--credentials { + flex-direction: row; + justify-content: flex-start; + align-items: center; + font-size: inherit; + font-weight: 500; + text-align: left; + letter-spacing: -0.25px; +} +.tooltip__button--credentials:first-child { + margin-top: 0; +} +.tooltip__button--credentials:last-child { + margin-bottom: 0; +} +.tooltip__button--credentials::before { + content: ''; + display: block; + width: 36px; + height: 36px; + margin: auto 6px auto 0; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+ZGRnLXBhc3N3b3JkLWljb24tYmFzZTwvdGl0bGU+CiAgICA8ZyBpZD0iZGRnLXBhc3N3b3JkLWljb24tYmFzZSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IlVuaW9uIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0LjAwMDAwMCwgNC4wMDAwMDApIiBmaWxsPSIjMDAwMDAwIj4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzMzMsMi42NjY2NyBDMTAuMjI4OCwyLjY2NjY3IDkuMzMzMzMsMy41NjIxIDkuMzMzMzMsNC42NjY2NyBDOS4zMzMzMyw1Ljc3MTI0IDEwLjIyODgsNi42NjY2NyAxMS4zMzMzLDYuNjY2NjcgQzEyLjQzNzksNi42NjY2NyAxMy4zMzMzLDUuNzcxMjQgMTMuMzMzMyw0LjY2NjY3IEMxMy4zMzMzLDMuNTYyMSAxMi40Mzc5LDIuNjY2NjcgMTEuMzMzMywyLjY2NjY3IFogTTEwLjY2NjcsNC42NjY2NyBDMTAuNjY2Nyw0LjI5ODQ4IDEwLjk2NTEsNCAxMS4zMzMzLDQgQzExLjcwMTUsNCAxMiw0LjI5ODQ4IDEyLDQuNjY2NjcgQzEyLDUuMDM0ODYgMTEuNzAxNSw1LjMzMzMzIDExLjMzMzMsNS4zMzMzMyBDMTAuOTY1MSw1LjMzMzMzIDEwLjY2NjcsNS4wMzQ4NiAxMC42NjY3LDQuNjY2NjcgWiIgaWQ9IlNoYXBlIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMC42NjY3LDAgQzcuNzIxMTUsMCA1LjMzMzMzLDIuMzg3ODEgNS4zMzMzMyw1LjMzMzMzIEM1LjMzMzMzLDUuNzYxMTkgNS4zODM4NSw2LjE3Nzk4IDUuNDc5NDUsNi41Nzc3NSBMMC4xOTUyNjIsMTEuODYxOSBDMC4wNzAyMzc5LDExLjk4NyAwLDEyLjE1NjUgMCwxMi4zMzMzIEwwLDE1LjMzMzMgQzAsMTUuNzAxNSAwLjI5ODQ3NywxNiAwLjY2NjY2NywxNiBMMy4zMzMzMywxNiBDNC4wNjk3MSwxNiA0LjY2NjY3LDE1LjQwMyA0LjY2NjY3LDE0LjY2NjcgTDQuNjY2NjcsMTQgTDUuMzMzMzMsMTQgQzYuMDY5NzEsMTQgNi42NjY2NywxMy40MDMgNi42NjY2NywxMi42NjY3IEw2LjY2NjY3LDExLjMzMzMgTDgsMTEuMzMzMyBDOC4xNzY4MSwxMS4zMzMzIDguMzQ2MzgsMTEuMjYzMSA4LjQ3MTQxLDExLjEzODEgTDkuMTU5MDYsMTAuNDUwNCBDOS42Mzc3MiwxMC41OTEyIDEwLjE0MzksMTAuNjY2NyAxMC42NjY3LDEwLjY2NjcgQzEzLjYxMjIsMTAuNjY2NyAxNiw4LjI3ODg1IDE2LDUuMzMzMzMgQzE2LDIuMzg3ODEgMTMuNjEyMiwwIDEwLjY2NjcsMCBaIE02LjY2NjY3LDUuMzMzMzMgQzYuNjY2NjcsMy4xMjQxOSA4LjQ1NzUzLDEuMzMzMzMgMTAuNjY2NywxLjMzMzMzIEMxMi44NzU4LDEuMzMzMzMgMTQuNjY2NywzLjEyNDE5IDE0LjY2NjcsNS4zMzMzMyBDMTQuNjY2Nyw3LjU0MjQ3IDEyLjg3NTgsOS4zMzMzMyAxMC42NjY3LDkuMzMzMzMgQzEwLjE1NTgsOS4zMzMzMyA5LjY2ODg2LDkuMjM3OSA5LjIyMTUyLDkuMDY0NSBDOC45NzUyOCw4Ljk2OTA1IDguNjk1OTEsOS4wMjc5NSA4LjUwOTE2LDkuMjE0NjkgTDcuNzIzODYsMTAgTDYsMTAgQzUuNjMxODEsMTAgNS4zMzMzMywxMC4yOTg1IDUuMzMzMzMsMTAuNjY2NyBMNS4zMzMzMywxMi42NjY3IEw0LDEyLjY2NjcgQzMuNjMxODEsMTIuNjY2NyAzLjMzMzMzLDEyLjk2NTEgMy4zMzMzMywxMy4zMzMzIEwzLjMzMzMzLDE0LjY2NjcgTDEuMzMzMzMsMTQuNjY2NyBMMS4zMzMzMywxMi42MDk1IEw2LjY5Nzg3LDcuMjQ0OTQgQzYuODc1MDIsNy4wNjc3OSA2LjkzNzksNi44MDYyOSA2Ljg2MDY1LDYuNTY3OTggQzYuNzM0ODksNi4xNzk5NyA2LjY2NjY3LDUuNzY1MjcgNi42NjY2Nyw1LjMzMzMzIFoiIGlkPSJTaGFwZSI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'); + background-size: cover; +} +.tooltip__button--credentials:hover::before { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+ZGRnLXBhc3N3b3JkLWljb24tYmFzZS13aGl0ZTwvdGl0bGU+CiAgICA8ZyBpZD0iZGRnLXBhc3N3b3JkLWljb24tYmFzZS13aGl0ZSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IlVuaW9uIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0LjAwMDAwMCwgNC4wMDAwMDApIiBmaWxsPSIjRkZGRkZGIj4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzMzMsMi42NjY2NyBDMTAuMjI4OCwyLjY2NjY3IDkuMzMzMzMsMy41NjIxIDkuMzMzMzMsNC42NjY2NyBDOS4zMzMzMyw1Ljc3MTI0IDEwLjIyODgsNi42NjY2NyAxMS4zMzMzLDYuNjY2NjcgQzEyLjQzNzksNi42NjY2NyAxMy4zMzMzLDUuNzcxMjQgMTMuMzMzMyw0LjY2NjY3IEMxMy4zMzMzLDMuNTYyMSAxMi40Mzc5LDIuNjY2NjcgMTEuMzMzMywyLjY2NjY3IFogTTEwLjY2NjcsNC42NjY2NyBDMTAuNjY2Nyw0LjI5ODQ4IDEwLjk2NTEsNCAxMS4zMzMzLDQgQzExLjcwMTUsNCAxMiw0LjI5ODQ4IDEyLDQuNjY2NjcgQzEyLDUuMDM0ODYgMTEuNzAxNSw1LjMzMzMzIDExLjMzMzMsNS4zMzMzMyBDMTAuOTY1MSw1LjMzMzMzIDEwLjY2NjcsNS4wMzQ4NiAxMC42NjY3LDQuNjY2NjcgWiIgaWQ9IlNoYXBlIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMC42NjY3LDAgQzcuNzIxMTUsMCA1LjMzMzMzLDIuMzg3ODEgNS4zMzMzMyw1LjMzMzMzIEM1LjMzMzMzLDUuNzYxMTkgNS4zODM4NSw2LjE3Nzk4IDUuNDc5NDUsNi41Nzc3NSBMMC4xOTUyNjIsMTEuODYxOSBDMC4wNzAyMzc5LDExLjk4NyAwLDEyLjE1NjUgMCwxMi4zMzMzIEwwLDE1LjMzMzMgQzAsMTUuNzAxNSAwLjI5ODQ3NywxNiAwLjY2NjY2NywxNiBMMy4zMzMzMywxNiBDNC4wNjk3MSwxNiA0LjY2NjY3LDE1LjQwMyA0LjY2NjY3LDE0LjY2NjcgTDQuNjY2NjcsMTQgTDUuMzMzMzMsMTQgQzYuMDY5NzEsMTQgNi42NjY2NywxMy40MDMgNi42NjY2NywxMi42NjY3IEw2LjY2NjY3LDExLjMzMzMgTDgsMTEuMzMzMyBDOC4xNzY4MSwxMS4zMzMzIDguMzQ2MzgsMTEuMjYzMSA4LjQ3MTQxLDExLjEzODEgTDkuMTU5MDYsMTAuNDUwNCBDOS42Mzc3MiwxMC41OTEyIDEwLjE0MzksMTAuNjY2NyAxMC42NjY3LDEwLjY2NjcgQzEzLjYxMjIsMTAuNjY2NyAxNiw4LjI3ODg1IDE2LDUuMzMzMzMgQzE2LDIuMzg3ODEgMTMuNjEyMiwwIDEwLjY2NjcsMCBaIE02LjY2NjY3LDUuMzMzMzMgQzYuNjY2NjcsMy4xMjQxOSA4LjQ1NzUzLDEuMzMzMzMgMTAuNjY2NywxLjMzMzMzIEMxMi44NzU4LDEuMzMzMzMgMTQuNjY2NywzLjEyNDE5IDE0LjY2NjcsNS4zMzMzMyBDMTQuNjY2Nyw3LjU0MjQ3IDEyLjg3NTgsOS4zMzMzMyAxMC42NjY3LDkuMzMzMzMgQzEwLjE1NTgsOS4zMzMzMyA5LjY2ODg2LDkuMjM3OSA5LjIyMTUyLDkuMDY0NSBDOC45NzUyOCw4Ljk2OTA1IDguNjk1OTEsOS4wMjc5NSA4LjUwOTE2LDkuMjE0NjkgTDcuNzIzODYsMTAgTDYsMTAgQzUuNjMxODEsMTAgNS4zMzMzMywxMC4yOTg1IDUuMzMzMzMsMTAuNjY2NyBMNS4zMzMzMywxMi42NjY3IEw0LDEyLjY2NjcgQzMuNjMxODEsMTIuNjY2NyAzLjMzMzMzLDEyLjk2NTEgMy4zMzMzMywxMy4zMzMzIEwzLjMzMzMzLDE0LjY2NjcgTDEuMzMzMzMsMTQuNjY2NyBMMS4zMzMzMywxMi42MDk1IEw2LjY5Nzg3LDcuMjQ0OTQgQzYuODc1MDIsNy4wNjc3OSA2LjkzNzksNi44MDYyOSA2Ljg2MDY1LDYuNTY3OTggQzYuNzM0ODksNi4xNzk5NyA2LjY2NjY3LDUuNzY1MjcgNi42NjY2Nyw1LjMzMzMzIFoiIGlkPSJTaGFwZSI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'); +} +.tooltip__button__password { + font-size: 11px; + color: rgba(0,0,0,0.6); +} +.tooltip__button:hover .tooltip__button__password { + color: #FFFFFF; +} + +/* Email tooltip specific */ +.tooltip__button--email { + flex-direction: column; + justify-content: center; + align-items: flex-start; + font-size: 14px; +} .tooltip__button__primary-text { font-weight: bold; } diff --git a/src/UI/CredentialsAutofill.js b/src/UI/CredentialsAutofill.js index c76c319a7..6a245bfbe 100644 --- a/src/UI/CredentialsAutofill.js +++ b/src/UI/CredentialsAutofill.js @@ -12,15 +12,15 @@ class CredentialsAutofill extends Tooltip { this.credentials = this.interface.getLocalCredentials() const includeStyles = isApp - ? `` + ? `` : `` this.shadow.innerHTML = ` ${includeStyles} -
-