From 86e534110e40a1a18da91ff7e7d26ec6612a9d37 Mon Sep 17 00:00:00 2001 From: Deepankar Bajpeyi Date: Thu, 9 Oct 2025 16:02:48 +0200 Subject: [PATCH 1/7] fix: add retry to userId parsing and a longer sleep before download --- injected/src/features/autofill-import.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/injected/src/features/autofill-import.js b/injected/src/features/autofill-import.js index eb0b202653..28500bb04d 100644 --- a/injected/src/features/autofill-import.js +++ b/injected/src/features/autofill-import.js @@ -591,9 +591,10 @@ export default class AutofillImport extends ActionExecutorBase { /** Bookmark import code */ async downloadData() { // sleep for a second, sometimes download link is not yet available - await new Promise((resolve) => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 2000)); - const userId = document.querySelector(this.bookmarkImportSelectorSettings.userIdLink)?.getAttribute('href')?.split('&user=')[1]; + const userIdElement = await this.runWithRetry(() => document.querySelector(this.bookmarkImportSelectorSettings.userIdLink)); + const userId = userIdElement?.getAttribute('href')?.split('&user=')[1]; await this.runWithRetry(() => document.querySelector(`a[href="./manage/archive/${this.#exportId}"]`), 15, 2000, 'linear'); if (userId != null && this.#exportId != null) { const downloadURL = `${TAKEOUT_DOWNLOAD_URL_BASE}?j=${this.#exportId}&i=0&user=${userId}`; From 7d43def0aa6a10bedc68f6a3bbd65ae116eea32b Mon Sep 17 00:00:00 2001 From: Deepankar Bajpeyi Date: Fri, 10 Oct 2025 13:41:31 +0200 Subject: [PATCH 2/7] fix: wait infinitely for download link --- injected/src/features/autofill-import.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/injected/src/features/autofill-import.js b/injected/src/features/autofill-import.js index 961dd6c5e2..fd07551d23 100644 --- a/injected/src/features/autofill-import.js +++ b/injected/src/features/autofill-import.js @@ -595,7 +595,11 @@ export default class AutofillImport extends ActionExecutorBase { const userIdElement = await this.runWithRetry(() => document.querySelector(this.bookmarkImportSelectorSettings.userIdLink)); const userId = userIdElement?.getAttribute('href')?.split('&user=')[1]; - await this.runWithRetry(() => document.querySelector(`a[href="./manage/archive/${this.#exportId}"]`), 15, 2000, 'linear'); + + // Poll forever until the download link is available, + // Android is the one that timesout anyway and closes the whole tab if this doesn't complete + await this.runWithRetry(() => document.querySelector(`a[href="./manage/archive/${this.#exportId}"]`), Infinity, 1000, 'linear'); + if (userId != null && this.#exportId != null) { const downloadURL = `${TAKEOUT_DOWNLOAD_URL_BASE}?j=${this.#exportId}&i=0&user=${userId}`; window.location.href = downloadURL; From 96ff15ce26a8826275a735f4055db48d6dbd7632 Mon Sep 17 00:00:00 2001 From: Deepankar Bajpeyi Date: Wed, 15 Oct 2025 15:41:36 +0200 Subject: [PATCH 3/7] fix: remove sleep and move retry limits to settings --- injected/src/features/autofill-import.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/injected/src/features/autofill-import.js b/injected/src/features/autofill-import.js index fd07551d23..cae646fb6c 100644 --- a/injected/src/features/autofill-import.js +++ b/injected/src/features/autofill-import.js @@ -590,15 +590,19 @@ export default class AutofillImport extends ActionExecutorBase { /** Bookmark import code */ async downloadData() { - // sleep for a second, sometimes download link is not yet available - await new Promise((resolve) => setTimeout(resolve, 2000)); - const userIdElement = await this.runWithRetry(() => document.querySelector(this.bookmarkImportSelectorSettings.userIdLink)); const userId = userIdElement?.getAttribute('href')?.split('&user=')[1]; // Poll forever until the download link is available, // Android is the one that timesout anyway and closes the whole tab if this doesn't complete - await this.runWithRetry(() => document.querySelector(`a[href="./manage/archive/${this.#exportId}"]`), Infinity, 1000, 'linear'); + const downloadRetryLimit = this.getFeatureSetting('downloadRetryLimit') ?? Infinity; + const downloadRetryInterval = this.getFeatureSetting('downloadRetryInterval') ?? 1000; + await this.runWithRetry( + () => document.querySelector(`a[href="./manage/archive/${this.#exportId}"]`), + downloadRetryLimit, + downloadRetryInterval, + 'linear', + ); if (userId != null && this.#exportId != null) { const downloadURL = `${TAKEOUT_DOWNLOAD_URL_BASE}?j=${this.#exportId}&i=0&user=${userId}`; From fb401e03ecab6a0823a8b70f9b91a89d53fa140e Mon Sep 17 00:00:00 2001 From: Deepankar Bajpeyi Date: Wed, 15 Oct 2025 16:16:00 +0200 Subject: [PATCH 4/7] refactor: poll forever as android will time out anyway --- injected/src/features/autofill-import.js | 33 +++++++++++++++--------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/injected/src/features/autofill-import.js b/injected/src/features/autofill-import.js index cae646fb6c..df6ba56ed9 100644 --- a/injected/src/features/autofill-import.js +++ b/injected/src/features/autofill-import.js @@ -590,31 +590,40 @@ export default class AutofillImport extends ActionExecutorBase { /** Bookmark import code */ async downloadData() { - const userIdElement = await this.runWithRetry(() => document.querySelector(this.bookmarkImportSelectorSettings.userIdLink)); - const userId = userIdElement?.getAttribute('href')?.split('&user=')[1]; - - // Poll forever until the download link is available, + // Run with retry forever until the download link is available, // Android is the one that timesout anyway and closes the whole tab if this doesn't complete const downloadRetryLimit = this.getFeatureSetting('downloadRetryLimit') ?? Infinity; const downloadRetryInterval = this.getFeatureSetting('downloadRetryInterval') ?? 1000; - await this.runWithRetry( - () => document.querySelector(`a[href="./manage/archive/${this.#exportId}"]`), + + const userIdElement = await this.runWithRetry( + () => document.querySelector(this.bookmarkImportSelectorSettings.userIdLink), downloadRetryLimit, downloadRetryInterval, 'linear', ); + const userIdLink = userIdElement?.getAttribute('href') ?? ''; + const userId = new URL(userIdLink, window.location.origin).searchParams.get('user'); - if (userId != null && this.#exportId != null) { - const downloadURL = `${TAKEOUT_DOWNLOAD_URL_BASE}?j=${this.#exportId}&i=0&user=${userId}`; - window.location.href = downloadURL; - } else { - // If there's no user id or export id, we post an action failed message + if (!userId || !this.#exportId) { this.postBookmarkImportMessage('actionCompleted', { result: new ErrorResponse({ actionID: 'download-data', - message: 'No user id or export id found', + message: 'User id or export id not found', }), }); + return; + } + + await this.runWithRetry( + () => document.querySelector(`a[href="./manage/archive/${this.#exportId}"]`), + downloadRetryLimit, + downloadRetryInterval, + 'linear', + ); + + if (userId != null && this.#exportId != null) { + const downloadURL = `${TAKEOUT_DOWNLOAD_URL_BASE}?j=${this.#exportId}&i=0&user=${userId}`; + window.location.href = downloadURL; } } From 04c9fda872de612a9f4fea782951ef3fb8fef625 Mon Sep 17 00:00:00 2001 From: Deepankar Bajpeyi Date: Wed, 15 Oct 2025 18:17:39 +0200 Subject: [PATCH 5/7] fix: check the link before parsing --- injected/src/features/autofill-import.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/injected/src/features/autofill-import.js b/injected/src/features/autofill-import.js index df6ba56ed9..cd0ed12f0b 100644 --- a/injected/src/features/autofill-import.js +++ b/injected/src/features/autofill-import.js @@ -601,8 +601,8 @@ export default class AutofillImport extends ActionExecutorBase { downloadRetryInterval, 'linear', ); - const userIdLink = userIdElement?.getAttribute('href') ?? ''; - const userId = new URL(userIdLink, window.location.origin).searchParams.get('user'); + const userIdLink = userIdElement?.getAttribute('href'); + const userId = userIdLink ? new URL(userIdLink, window.location.origin).searchParams.get('user') : null; if (!userId || !this.#exportId) { this.postBookmarkImportMessage('actionCompleted', { @@ -621,10 +621,8 @@ export default class AutofillImport extends ActionExecutorBase { 'linear', ); - if (userId != null && this.#exportId != null) { - const downloadURL = `${TAKEOUT_DOWNLOAD_URL_BASE}?j=${this.#exportId}&i=0&user=${userId}`; - window.location.href = downloadURL; - } + const downloadURL = `${TAKEOUT_DOWNLOAD_URL_BASE}?j=${this.#exportId}&i=0&user=${userId}`; + window.location.href = downloadURL; } /** From 59949960fd90da1f50f147f767ac9cc182c4cf32 Mon Sep 17 00:00:00 2001 From: Deepankar Bajpeyi Date: Fri, 17 Oct 2025 17:46:31 +0530 Subject: [PATCH 6/7] fix: sleep before download --- injected/src/features/autofill-import.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/injected/src/features/autofill-import.js b/injected/src/features/autofill-import.js index cd0ed12f0b..34a7e5d3f0 100644 --- a/injected/src/features/autofill-import.js +++ b/injected/src/features/autofill-import.js @@ -622,6 +622,10 @@ export default class AutofillImport extends ActionExecutorBase { ); const downloadURL = `${TAKEOUT_DOWNLOAD_URL_BASE}?j=${this.#exportId}&i=0&user=${userId}`; + + // Sleep before downloading to ensure the download link is available + await new Promise((resolve) => setTimeout(resolve, 2000)); + window.location.href = downloadURL; } From a0a97d845481c2729141fae879974e7e695fd870 Mon Sep 17 00:00:00 2001 From: Deepankar Bajpeyi Date: Fri, 17 Oct 2025 17:56:33 +0530 Subject: [PATCH 7/7] refactor: move sleep to config --- injected/src/features/autofill-import.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/injected/src/features/autofill-import.js b/injected/src/features/autofill-import.js index 34a7e5d3f0..9710a5ac42 100644 --- a/injected/src/features/autofill-import.js +++ b/injected/src/features/autofill-import.js @@ -624,7 +624,8 @@ export default class AutofillImport extends ActionExecutorBase { const downloadURL = `${TAKEOUT_DOWNLOAD_URL_BASE}?j=${this.#exportId}&i=0&user=${userId}`; // Sleep before downloading to ensure the download link is available - await new Promise((resolve) => setTimeout(resolve, 2000)); + const downloadNavigationDelayMs = this.getFeatureSetting('downloadNavigationDelayMs') ?? 2000; + await new Promise((resolve) => setTimeout(resolve, downloadNavigationDelayMs)); window.location.href = downloadURL; }