Skip to content

Commit

Permalink
feat: add exponential backoff and retry
Browse files Browse the repository at this point in the history
  • Loading branch information
dbajpeyi committed Sep 25, 2024
1 parent fd0312a commit df0d366
Showing 1 changed file with 52 additions and 23 deletions.
75 changes: 52 additions & 23 deletions src/features/password-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,81 @@ import ContentFeature from '../content-feature'
import { DDGProxy, DDGReflect } from '../utils'
import { getElement } from './broker-protection/utils'

const MAX_SEARCH_ATTEMPTS = 10
const INITIAL_ATTEMPT_DELAY = 500 // in ms

export default class PasswordImport extends ContentFeature {
searchElements () {
const xpath = "//div[text()='Export passwords']/ancestor::li" // Should be configurable
const exportElement = getElement(document, xpath)
if (exportElement) {
exportElement.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center'
}) // Scroll into view
const keyframes = [
{ backgroundColor: 'transparent' },
{ backgroundColor: 'lightblue' },
{ backgroundColor: 'transparent' }
]
return new Promise((resolve, reject) => {
const xpath = "//div[text()='Export passwords']/ancestor::li" // Should be configurable
const exportElement = getElement(document, xpath)
if (exportElement) {
if (exportElement) {
exportElement.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center'
}) // Scroll into view
const keyframes = [
{ backgroundColor: 'transparent' },
{ backgroundColor: 'lightblue' },
{ backgroundColor: 'transparent' }
]

// Define the animation options
const options = {
duration: 1000, // 1 seconds, should be configurable
iterations: 3 // Max 3 blinks, should be configurable
}

// Define the animation options
const options = {
duration: 1000, // 1 seconds, should be configurable
iterations: 3 // Max 3 blinks, should be configurable
// Apply the animation to the element
exportElement.animate(keyframes, options)
resolve(exportElement)
}
} else {
reject(new Error('Export passwords element not found'))
}
})
}

// Apply the animation to the element
exportElement.animate(keyframes, options)
}
withExponentialBackoff (fn, maxAttempts = MAX_SEARCH_ATTEMPTS, delay = INITIAL_ATTEMPT_DELAY) {
return new Promise((resolve, reject) => {
const call = (attempt) => {
try {
const result = fn()
resolve(result)
} catch (error) {
if (attempt >= maxAttempts) {
reject(error)
} else {
setTimeout(() => call(attempt + 1), delay * 2 ** attempt)
}
}
}
call(0)
})
}

init () {
const searchElements = this.searchElements.bind(this)
const withExponentialBackoff = this.withExponentialBackoff.bind(this)
// FIXME: this is stolen from element-hiding.js, we would need a global util that would do this,
// single page applications don't have a DOMContentLoaded event on navigations, so
// we use proxy/reflect on history.pushState to call applyRules on page navigations
const historyMethodProxy = new DDGProxy(this, History.prototype, 'pushState', {
apply (target, thisArg, args) {
searchElements()
withExponentialBackoff(searchElements)
return DDGReflect.apply(target, thisArg, args)
}
})
historyMethodProxy.overload()
// listen for popstate events in order to run on back/forward navigations
window.addEventListener('popstate', () => {
searchElements()
withExponentialBackoff(searchElements)
})

document.addEventListener('DOMContentLoaded', () => {
searchElements()
withExponentialBackoff(searchElements)
})
}
}

0 comments on commit df0d366

Please sign in to comment.