Skip to content

Commit c2ed781

Browse files
authored
[Autofill Import] Navigate to manage page and retry download (#2026)
* fix: configurable retry * fix: go straight to the archive page * refactor: don't need to store export ID * fix: filter empty segments
1 parent 0f12a52 commit c2ed781

File tree

1 file changed

+39
-37
lines changed

1 file changed

+39
-37
lines changed

injected/src/features/autofill-import.js

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const BACKGROUND_COLOR_START = 'rgba(85, 127, 243, 0.10)';
88
export const BACKGROUND_COLOR_END = 'rgba(85, 127, 243, 0.25)';
99
export const OVERLAY_ID = 'ddg-password-import-overlay';
1010
export const DELAY_BEFORE_ANIMATION = 300;
11-
const TAKEOUT_DOWNLOAD_URL_BASE = '/takeout/download';
11+
const MANAGE_ARCHIVE_DEFAULT_BASE = '/manage/archive';
1212

1313
/**
1414
* @typedef ButtonAnimationStyle
@@ -55,8 +55,6 @@ export default class AutofillImport extends ActionExecutorBase {
5555

5656
#domLoaded;
5757

58-
#exportId;
59-
6058
#processingBookmark;
6159

6260
#isBookmarkModalVisible = false;
@@ -589,22 +587,23 @@ export default class AutofillImport extends ActionExecutorBase {
589587
}
590588

591589
/** Bookmark import code */
590+
get defaultRetrySettings() {
591+
return {
592+
maxAttempts: this.getFeatureSetting('downloadRetryLimit') ?? Infinity,
593+
interval: this.getFeatureSetting('downloadRetryInterval') ?? 1000,
594+
};
595+
}
596+
592597
async downloadData() {
593598
// Run with retry forever until the download link is available,
594599
// Android is the one that timesout anyway and closes the whole tab if this doesn't complete
595-
const downloadRetryLimit = this.getFeatureSetting('downloadRetryLimit') ?? Infinity;
596-
const downloadRetryInterval = this.getFeatureSetting('downloadRetryInterval') ?? 1000;
597-
598-
const userIdElement = await this.runWithRetry(
599-
() => document.querySelector(this.bookmarkImportSelectorSettings.userIdLink),
600-
downloadRetryLimit,
601-
downloadRetryInterval,
602-
'linear',
603-
);
604-
const userIdLink = userIdElement?.getAttribute('href');
605-
const userId = userIdLink ? new URL(userIdLink, window.location.origin).searchParams.get('user') : null;
606600

607-
if (!userId || !this.#exportId) {
601+
const exportId = window.location.pathname
602+
.split('/')
603+
.filter((segment) => segment)
604+
.pop();
605+
606+
if (!exportId) {
608607
this.postBookmarkImportMessage('actionCompleted', {
609608
result: new ErrorResponse({
610609
actionID: 'download-data',
@@ -614,30 +613,28 @@ export default class AutofillImport extends ActionExecutorBase {
614613
return;
615614
}
616615

617-
await this.runWithRetry(
618-
() => document.querySelector(`a[href="./manage/archive/${this.#exportId}"]`),
619-
downloadRetryLimit,
620-
downloadRetryInterval,
621-
'linear',
616+
const downloadLinkSelector = this.bookmarkImportSelectorSettings.downloadLink ?? `a[href*="&i=0&user="]`;
617+
const downloadButton = /** @type {HTMLAnchorElement|null} */ (
618+
await this.runWithRetry(() => document.querySelector(downloadLinkSelector), 5, 1000, 'linear')
622619
);
620+
if (downloadButton == null) {
621+
// If there was no download link, it was likely a 404
622+
// so we reload the page to try again
623+
window.location.reload();
624+
}
623625

624-
const downloadURL = `${TAKEOUT_DOWNLOAD_URL_BASE}?j=${this.#exportId}&i=0&user=${userId}`;
625-
626-
// Sleep before downloading to ensure the download link is available
627-
const downloadNavigationDelayMs = this.getFeatureSetting('downloadNavigationDelayMs') ?? 2000;
628-
await new Promise((resolve) => setTimeout(resolve, downloadNavigationDelayMs));
629-
630-
window.location.href = downloadURL;
626+
downloadButton?.click();
631627
}
632628

633629
/**
634630
* Here we ignore the action and return a default retry config
635631
* as for now the retry doesn't need to be per action.
636632
*/
637633
retryConfigFor(_) {
634+
const { interval, maxAttempts } = this.defaultRetrySettings;
638635
return {
639-
interval: { ms: 1000 },
640-
maxAttempts: 30,
636+
interval: { ms: interval },
637+
maxAttempts,
641638
};
642639
}
643640

@@ -660,14 +657,17 @@ export default class AutofillImport extends ActionExecutorBase {
660657
async handleBookmarkImportPath(pathname) {
661658
if (pathname === '/' && !this.#isBookmarkModalVisible) {
662659
for (const action of this.bookmarkImportActionSettings) {
663-
// Before clicking on the manage button, we need to store the export id
664-
if (action.id === 'manage-button-click') {
665-
await this.storeExportId();
666-
}
667-
668660
await this.patchMessagingAndProcessAction(action);
669661
}
662+
663+
// Parse the export id from the page and then navigate to the 'manage' page
664+
const exportId = await this.getExportId();
665+
window.location.href = `${MANAGE_ARCHIVE_DEFAULT_BASE}/${exportId}`;
666+
} else if (pathname.startsWith(MANAGE_ARCHIVE_DEFAULT_BASE)) {
667+
// If we're on the 'manage' page, we can download the data
670668
await this.downloadData();
669+
} else {
670+
// Unhandled path, we bail out
671671
}
672672
}
673673

@@ -681,11 +681,13 @@ export default class AutofillImport extends ActionExecutorBase {
681681
findExportId() {
682682
const panels = document.querySelectorAll(this.bookmarkImportSelectorSettings.tabPanel);
683683
const exportPanel = panels[panels.length - 1];
684-
return exportPanel.querySelector('div[data-archive-id]')?.getAttribute('data-archive-id');
684+
const dataArchiveIdSelector = this.bookmarkImportSelectorSettings.dataArchiveId ?? `div[data-archive-id]`;
685+
return exportPanel.querySelector(dataArchiveIdSelector)?.getAttribute('data-archive-id');
685686
}
686687

687-
async storeExportId() {
688-
this.#exportId = await this.runWithRetry(() => this.findExportId(), 30, 1000, 'linear');
688+
async getExportId() {
689+
const { maxAttempts, interval } = this.defaultRetrySettings;
690+
return await this.runWithRetry(() => this.findExportId(), maxAttempts, interval, 'linear');
689691
}
690692

691693
urlChanged() {

0 commit comments

Comments
 (0)