From a90a5cba08fdf6c0ceb95101c275108a152a3aed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:35:37 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- .../migration/ChromeProfileMigrator.sys.mjs | 3 +- .../components/migration/MSMigrationUtils.sys.mjs | 2 +- .../components/migration/MigrationUtils.sys.mjs | 49 +++-- .../migration/SafariProfileMigrator.sys.mjs | 4 +- .../migration/content/migration-wizard.mjs | 206 +++++++++++---------- .../tests/browser/browser_disabled_migrator.js | 4 +- .../tests/browser/browser_do_migration.js | 2 +- .../tests/browser/browser_file_migration.js | 4 +- browser/components/migration/tests/browser/head.js | 2 +- .../tests/chrome/test_migration_wizard.html | 4 +- .../migration/tests/unit/head_migration.js | 28 +++ .../migration/tests/unit/test_Chrome_bookmarks.js | 20 +- .../unit/test_Safari_history_strange_entries.js | 7 +- 13 files changed, 203 insertions(+), 132 deletions(-) (limited to 'browser/components/migration') diff --git a/browser/components/migration/ChromeProfileMigrator.sys.mjs b/browser/components/migration/ChromeProfileMigrator.sys.mjs index e32417cd04..17aba35e8a 100644 --- a/browser/components/migration/ChromeProfileMigrator.sys.mjs +++ b/browser/components/migration/ChromeProfileMigrator.sys.mjs @@ -785,7 +785,8 @@ async function GetBookmarksResource(aProfileFolder, aBrowserKey) { } // Import Bookmark Favicons - MigrationUtils.insertManyFavicons(favicons); + MigrationUtils.insertManyFavicons(favicons).catch(console.error); + if (gotErrors) { throw new Error("The migration included errors."); } diff --git a/browser/components/migration/MSMigrationUtils.sys.mjs b/browser/components/migration/MSMigrationUtils.sys.mjs index 8d9a666e66..37dd69bf10 100644 --- a/browser/components/migration/MSMigrationUtils.sys.mjs +++ b/browser/components/migration/MSMigrationUtils.sys.mjs @@ -381,7 +381,7 @@ Bookmarks.prototype = { } await MigrationUtils.insertManyBookmarksWrapper(bookmarks, aDestFolderGuid); - MigrationUtils.insertManyFavicons(favicons); + MigrationUtils.insertManyFavicons(favicons).catch(console.error); }, /** diff --git a/browser/components/migration/MigrationUtils.sys.mjs b/browser/components/migration/MigrationUtils.sys.mjs index cda3028cc4..90ba6a535e 100644 --- a/browser/components/migration/MigrationUtils.sys.mjs +++ b/browser/components/migration/MigrationUtils.sys.mjs @@ -870,36 +870,53 @@ class MigrationUtils { * Iterates through the favicons, sniffs for a mime type, * and uses the mime type to properly import the favicon. * + * Note: You may not want to await on the returned promise, especially if by + * doing so there's risk of interrupting the migration of more critical + * data (e.g. bookmarks). + * * @param {object[]} favicons * An array of Objects with these properties: * {Uint8Array} faviconData: The binary data of a favicon * {nsIURI} uri: The URI of the associated page */ - insertManyFavicons(favicons) { + async insertManyFavicons(favicons) { let sniffer = Cc["@mozilla.org/image/loader;1"].createInstance( Ci.nsIContentSniffer ); + for (let faviconDataItem of favicons) { - let mimeType = sniffer.getMIMETypeFromContent( - null, - faviconDataItem.faviconData, - faviconDataItem.faviconData.length - ); + let dataURL; + + try { + // getMIMETypeFromContent throws error if could not get the mime type + // from the data. + let mimeType = sniffer.getMIMETypeFromContent( + null, + faviconDataItem.faviconData, + faviconDataItem.faviconData.length + ); + + dataURL = await new Promise((resolve, reject) => { + let buffer = new Uint8ClampedArray(faviconDataItem.faviconData); + let blob = new Blob([buffer], { type: mimeType }); + let reader = new FileReader(); + reader.addEventListener("load", () => resolve(reader.result)); + reader.addEventListener("error", reject); + reader.readAsDataURL(blob); + }); + } catch (e) { + // Even if error happens for favicon, continue the process. + console.warn(e); + continue; + } + let fakeFaviconURI = Services.io.newURI( "fake-favicon-uri:" + faviconDataItem.uri.spec ); - lazy.PlacesUtils.favicons.replaceFaviconData( - fakeFaviconURI, - faviconDataItem.faviconData, - mimeType - ); - lazy.PlacesUtils.favicons.setAndFetchFaviconForPage( + lazy.PlacesUtils.favicons.setFaviconForPage( faviconDataItem.uri, fakeFaviconURI, - true, - lazy.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, - null, - Services.scriptSecurityManager.getSystemPrincipal() + Services.io.newURI(dataURL) ); } } diff --git a/browser/components/migration/SafariProfileMigrator.sys.mjs b/browser/components/migration/SafariProfileMigrator.sys.mjs index c134c0869a..307edbd230 100644 --- a/browser/components/migration/SafariProfileMigrator.sys.mjs +++ b/browser/components/migration/SafariProfileMigrator.sys.mjs @@ -98,7 +98,7 @@ Bookmarks.prototype = { let rows = await MigrationUtils.getRowsFromDBWithoutLocks( dbPath, "Safari favicons", - `SELECT I.uuid, I.url AS favicon_url, P.url + `SELECT I.uuid, I.url AS favicon_url, P.url FROM icon_info I INNER JOIN page_url P ON I.uuid = P.uuid;` ); @@ -253,7 +253,7 @@ Bookmarks.prototype = { parentGuid ); - MigrationUtils.insertManyFavicons(favicons); + MigrationUtils.insertManyFavicons(favicons).catch(console.error); }, /** diff --git a/browser/components/migration/content/migration-wizard.mjs b/browser/components/migration/content/migration-wizard.mjs index 89872a1558..9d58fbe95f 100644 --- a/browser/components/migration/content/migration-wizard.mjs +++ b/browser/components/migration/content/migration-wizard.mjs @@ -333,6 +333,7 @@ export class MigrationWizard extends HTMLElement { this.#getPermissionsButton.addEventListener("click", this); this.#browserProfileSelector.addEventListener("click", this); + this.#browserProfileSelector.addEventListener("mousedown", this); this.#resourceTypeList = shadow.querySelector("#resource-type-list"); this.#resourceTypeList.addEventListener("change", this); @@ -1396,107 +1397,122 @@ export class MigrationWizard extends HTMLElement { } } + #handleClickEvent(event) { + if ( + event.target == this.#importButton || + event.target == this.#importFromFileButton + ) { + this.#doImport(); + } else if ( + event.target.classList.contains("cancel-close") || + event.target.classList.contains("finish-button") + ) { + this.dispatchEvent( + new CustomEvent("MigrationWizard:Close", { bubbles: true }) + ); + } else if ( + event.currentTarget == this.#browserProfileSelectorList && + event.target != this.#browserProfileSelectorList + ) { + this.#onBrowserProfileSelectionChanged(event.target); + // If the user selected a file migration type from the selector, we'll + // help the user out by immediately starting the file migration flow, + // rather than waiting for them to click the "Select File". + if ( + event.target.getAttribute("type") == + MigrationWizardConstants.MIGRATOR_TYPES.FILE + ) { + this.#doImport(); + } + } else if (event.target == this.#safariPermissionButton) { + this.#requestSafariPermissions(); + } else if (event.currentTarget == this.#resourceSummary) { + this.#expandedDetails = true; + } else if (event.target == this.#chooseImportFromFile) { + this.dispatchEvent( + new CustomEvent("MigrationWizard:RequestState", { + bubbles: true, + detail: { + allowOnlyFileMigrators: true, + }, + }) + ); + } else if (event.target == this.#safariPasswordImportSkipButton) { + // If the user chose to skip importing passwords from Safari, we + // programmatically uncheck the PASSWORDS resource type and re-request + // import. + let checkbox = this.#shadowRoot.querySelector( + `label[data-resource-type="${MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS}"]` + ).control; + checkbox.checked = false; + + // If there are no other checked checkboxes, go back to the selection + // screen. + let checked = this.#shadowRoot.querySelectorAll( + `label[data-resource-type] > input:checked` + ).length; + + if (!checked) { + this.requestState(); + } else { + this.#doImport(); + } + } else if (event.target == this.#safariPasswordImportSelectButton) { + this.#selectSafariPasswordFile(); + } else if (event.target == this.#extensionsSuccessLink) { + this.dispatchEvent( + new CustomEvent("MigrationWizard:OpenAboutAddons", { + bubbles: true, + }) + ); + event.preventDefault(); + } else if (event.target == this.#getPermissionsButton) { + this.#getPermissions(); + } + } + + #handleChangeEvent(event) { + if (event.target == this.#browserProfileSelector) { + this.#onBrowserProfileSelectionChanged(); + } else if (event.target == this.#selectAllCheckbox) { + let checkboxes = this.#shadowRoot.querySelectorAll( + 'label[data-resource-type]:not([hidden]) > input[type="checkbox"]' + ); + for (let checkbox of checkboxes) { + checkbox.checked = this.#selectAllCheckbox.checked; + } + this.#displaySelectedResources(); + } else { + let checkboxes = this.#shadowRoot.querySelectorAll( + 'label[data-resource-type]:not([hidden]) > input[type="checkbox"]' + ); + + let allVisibleChecked = Array.from(checkboxes).every(checkbox => { + return checkbox.checked; + }); + + this.#selectAllCheckbox.checked = allVisibleChecked; + this.#displaySelectedResources(); + } + } + handleEvent(event) { + if ( + event.target == this.#browserProfileSelector && + (event.type == "mousedown" || + (event.type == "click" && + event.mozInputSource == MouseEvent.MOZ_SOURCE_KEYBOARD)) + ) { + this.#browserProfileSelectorList.toggle(event); + return; + } switch (event.type) { case "click": { - if ( - event.target == this.#importButton || - event.target == this.#importFromFileButton - ) { - this.#doImport(); - } else if ( - event.target.classList.contains("cancel-close") || - event.target.classList.contains("finish-button") - ) { - this.dispatchEvent( - new CustomEvent("MigrationWizard:Close", { bubbles: true }) - ); - } else if (event.target == this.#browserProfileSelector) { - this.#browserProfileSelectorList.show(event); - } else if ( - event.currentTarget == this.#browserProfileSelectorList && - event.target != this.#browserProfileSelectorList - ) { - this.#onBrowserProfileSelectionChanged(event.target); - // If the user selected a file migration type from the selector, we'll - // help the user out by immediately starting the file migration flow, - // rather than waiting for them to click the "Select File". - if ( - event.target.getAttribute("type") == - MigrationWizardConstants.MIGRATOR_TYPES.FILE - ) { - this.#doImport(); - } - } else if (event.target == this.#safariPermissionButton) { - this.#requestSafariPermissions(); - } else if (event.currentTarget == this.#resourceSummary) { - this.#expandedDetails = true; - } else if (event.target == this.#chooseImportFromFile) { - this.dispatchEvent( - new CustomEvent("MigrationWizard:RequestState", { - bubbles: true, - detail: { - allowOnlyFileMigrators: true, - }, - }) - ); - } else if (event.target == this.#safariPasswordImportSkipButton) { - // If the user chose to skip importing passwords from Safari, we - // programmatically uncheck the PASSWORDS resource type and re-request - // import. - let checkbox = this.#shadowRoot.querySelector( - `label[data-resource-type="${MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS}"]` - ).control; - checkbox.checked = false; - - // If there are no other checked checkboxes, go back to the selection - // screen. - let checked = this.#shadowRoot.querySelectorAll( - `label[data-resource-type] > input:checked` - ).length; - - if (!checked) { - this.requestState(); - } else { - this.#doImport(); - } - } else if (event.target == this.#safariPasswordImportSelectButton) { - this.#selectSafariPasswordFile(); - } else if (event.target == this.#extensionsSuccessLink) { - this.dispatchEvent( - new CustomEvent("MigrationWizard:OpenAboutAddons", { - bubbles: true, - }) - ); - event.preventDefault(); - } else if (event.target == this.#getPermissionsButton) { - this.#getPermissions(); - } + this.#handleClickEvent(event); break; } case "change": { - if (event.target == this.#browserProfileSelector) { - this.#onBrowserProfileSelectionChanged(); - } else if (event.target == this.#selectAllCheckbox) { - let checkboxes = this.#shadowRoot.querySelectorAll( - 'label[data-resource-type]:not([hidden]) > input[type="checkbox"]' - ); - for (let checkbox of checkboxes) { - checkbox.checked = this.#selectAllCheckbox.checked; - } - this.#displaySelectedResources(); - } else { - let checkboxes = this.#shadowRoot.querySelectorAll( - 'label[data-resource-type]:not([hidden]) > input[type="checkbox"]' - ); - - let allVisibleChecked = Array.from(checkboxes).every(checkbox => { - return checkbox.checked; - }); - - this.#selectAllCheckbox.checked = allVisibleChecked; - this.#displaySelectedResources(); - } + this.#handleChangeEvent(event); break; } } diff --git a/browser/components/migration/tests/browser/browser_disabled_migrator.js b/browser/components/migration/tests/browser/browser_disabled_migrator.js index 782666f6a6..a9a6b3083c 100644 --- a/browser/components/migration/tests/browser/browser_disabled_migrator.js +++ b/browser/components/migration/tests/browser/browser_disabled_migrator.js @@ -17,7 +17,7 @@ add_task(async function test_enabled_migrator() { let wizard = dialog.querySelector("migration-wizard"); let shadow = wizard.openOrClosedShadowRoot; let selector = shadow.querySelector("#browser-profile-selector"); - selector.click(); + EventUtils.synthesizeMouseAtCenter(selector, {}, prefsWin); await new Promise(resolve => { shadow @@ -78,7 +78,7 @@ add_task(async function test_disabling_migrator() { let wizard = dialog.querySelector("migration-wizard"); let shadow = wizard.openOrClosedShadowRoot; let selector = shadow.querySelector("#browser-profile-selector"); - selector.click(); + EventUtils.synthesizeMouseAtCenter(selector, {}, prefsWin); await new Promise(resolve => { shadow diff --git a/browser/components/migration/tests/browser/browser_do_migration.js b/browser/components/migration/tests/browser/browser_do_migration.js index fab9641960..74454c0ab1 100644 --- a/browser/components/migration/tests/browser/browser_do_migration.js +++ b/browser/components/migration/tests/browser/browser_do_migration.js @@ -106,7 +106,7 @@ add_task(async function test_successful_migrations() { ); let dialogClosed = BrowserTestUtils.waitForEvent(dialog, "close"); - doneButton.click(); + EventUtils.synthesizeMouseAtCenter(doneButton, {}, prefsWin); await dialogClosed; assertQuantitiesShown(wizard, [ MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS, diff --git a/browser/components/migration/tests/browser/browser_file_migration.js b/browser/components/migration/tests/browser/browser_file_migration.js index c73dfc4456..94f4ff2908 100644 --- a/browser/components/migration/tests/browser/browser_file_migration.js +++ b/browser/components/migration/tests/browser/browser_file_migration.js @@ -132,7 +132,7 @@ add_task(async function test_file_migration() { // Now select our DummyFileMigrator from the list. let selector = shadow.querySelector("#browser-profile-selector"); - selector.click(); + EventUtils.synthesizeMouseAtCenter(selector, {}, prefsWin); info("Waiting for panel-list shown"); await new Promise(resolve => { @@ -246,7 +246,7 @@ add_task(async function test_file_migration_error() { // Now select our DummyFileMigrator from the list. let selector = shadow.querySelector("#browser-profile-selector"); - selector.click(); + EventUtils.synthesizeMouseAtCenter(selector, {}, prefsWin); info("Waiting for panel-list shown"); await new Promise(resolve => { diff --git a/browser/components/migration/tests/browser/head.js b/browser/components/migration/tests/browser/head.js index d3d188a7e1..8824a50ee9 100644 --- a/browser/components/migration/tests/browser/head.js +++ b/browser/components/migration/tests/browser/head.js @@ -332,7 +332,7 @@ async function selectResourceTypesAndStartMigration( // First, select the InternalTestingProfileMigrator browser. let selector = shadow.querySelector("#browser-profile-selector"); - selector.click(); + EventUtils.synthesizeMouseAtCenter(selector, {}, wizard.ownerGlobal); await new Promise(resolve => { shadow diff --git a/browser/components/migration/tests/chrome/test_migration_wizard.html b/browser/components/migration/tests/chrome/test_migration_wizard.html index cc2d8a0363..43fd3ab931 100644 --- a/browser/components/migration/tests/chrome/test_migration_wizard.html +++ b/browser/components/migration/tests/chrome/test_migration_wizard.html @@ -147,7 +147,7 @@ // Test that the resource type checkboxes are shown or hidden depending on // which resourceTypes are included with the MigratorProfileInstance. for (let migratorInstance of MIGRATOR_PROFILE_INSTANCES) { - selector.click(); + synthesizeMouseAtCenter(selector, {}, gWiz.ownerGlobal); await new Promise(resolve => { gShadowRoot .querySelector("panel-list") @@ -248,7 +248,7 @@ ok(isHidden(preamble), "preamble should be hidden."); let selector = gShadowRoot.querySelector("#browser-profile-selector"); - selector.click(); + synthesizeMouseAtCenter(selector, {}, gWiz.ownerGlobal); await new Promise(resolve => { let panelList = gShadowRoot.querySelector("panel-list"); if (panelList) { diff --git a/browser/components/migration/tests/unit/head_migration.js b/browser/components/migration/tests/unit/head_migration.js index 9900f34232..9b056e6670 100644 --- a/browser/components/migration/tests/unit/head_migration.js +++ b/browser/components/migration/tests/unit/head_migration.js @@ -117,6 +117,34 @@ async function assertFavicons(pageURIs) { } } +/** + * Check the image data for favicon of given page uri. + * + * @param {string} pageURI + * The page URI to which the favicon belongs. + * @param {Array} expectedImageData + * Expected image data of the favicon. + * @param {string} expectedMimeType + * Expected mime type of the favicon. + */ +async function assertFavicon(pageURI, expectedImageData, expectedMimeType) { + let result = await new Promise(resolve => { + PlacesUtils.favicons.getFaviconDataForPage( + Services.io.newURI(pageURI), + (faviconURI, dataLen, imageData, mimeType) => { + resolve({ faviconURI, dataLen, imageData, mimeType }); + } + ); + }); + Assert.ok(!!result, `Got favicon for ${pageURI}`); + Assert.equal( + result.imageData.join(","), + expectedImageData.join(","), + "Image data is correct" + ); + Assert.equal(result.mimeType, expectedMimeType, "Mime type is correct"); +} + /** * Replaces a directory service entry with a given nsIFile. * diff --git a/browser/components/migration/tests/unit/test_Chrome_bookmarks.js b/browser/components/migration/tests/unit/test_Chrome_bookmarks.js index d115cda412..3c09869800 100644 --- a/browser/components/migration/tests/unit/test_Chrome_bookmarks.js +++ b/browser/components/migration/tests/unit/test_Chrome_bookmarks.js @@ -71,11 +71,13 @@ async function testBookmarks(migratorKey, subDirs) { ).path; await IOUtils.copy(sourcePath, target.path); - // Get page url for each favicon - let faviconURIs = await MigrationUtils.getRowsFromDBWithoutLocks( + // Get page url and the image data for each favicon + let favicons = await MigrationUtils.getRowsFromDBWithoutLocks( sourcePath, "Chrome Bookmark Favicons", - `select page_url from icon_mapping` + `SELECT page_url, image_data FROM icon_mapping + INNER JOIN favicon_bitmaps ON (favicon_bitmaps.icon_id = icon_mapping.icon_id) + ` ); target.append("Bookmarks"); @@ -171,10 +173,14 @@ async function testBookmarks(migratorKey, subDirs) { "Telemetry reporting correct." ); Assert.ok(observerNotified, "The observer should be notified upon migration"); - let pageUrls = Array.from(faviconURIs, f => - Services.io.newURI(f.getResultByName("page_url")) - ); - await assertFavicons(pageUrls); + + for (const favicon of favicons) { + await assertFavicon( + favicon.getResultByName("page_url"), + favicon.getResultByName("image_data"), + "image/png" + ); + } } add_task(async function test_Chrome() { diff --git a/browser/components/migration/tests/unit/test_Safari_history_strange_entries.js b/browser/components/migration/tests/unit/test_Safari_history_strange_entries.js index 2578353e35..a22e6e1655 100644 --- a/browser/components/migration/tests/unit/test_Safari_history_strange_entries.js +++ b/browser/components/migration/tests/unit/test_Safari_history_strange_entries.js @@ -74,7 +74,7 @@ add_task(async function testHistoryImportStrangeEntries() { await PlacesUtils.history.clear(); let placesQuery = new PlacesQuery(); - let emptyHistory = await placesQuery.getHistory(); + let emptyHistory = await placesQuery.getHistory({ daysOld: Infinity }); Assert.equal(emptyHistory.size, 0, "Empty history should indeed be empty."); const EXPECTED_MIGRATED_SITES = 10; @@ -94,7 +94,10 @@ add_task(async function testHistoryImportStrangeEntries() { let migrator = await MigrationUtils.getMigrator("safari"); await promiseMigration(migrator, MigrationUtils.resourceTypes.HISTORY); - let migratedHistory = await placesQuery.getHistory({ sortBy: "site" }); + let migratedHistory = await placesQuery.getHistory({ + daysOld: Infinity, + sortBy: "site", + }); let siteCount = migratedHistory.size; let visitCount = 0; for (let [, visits] of migratedHistory) { -- cgit v1.2.3