Skip to content

Commit 66db388

Browse files
committed
Bug 1866772 - Be more resilient against malformed URLs when importing from Safari. r=mstriemer
Differential Revision: https://phabricator.services.mozilla.com/D196100
1 parent 7d49943 commit 66db388

File tree

4 files changed

+131
-10
lines changed

4 files changed

+131
-10
lines changed

browser/components/migration/SafariProfileMigrator.sys.mjs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -425,16 +425,20 @@ async function GetHistoryResource() {
425425

426426
let pageInfos = [];
427427
for (let row of historyRows) {
428-
pageInfos.push({
429-
title: row.getResultByName("history_title"),
430-
url: new URL(row.getResultByName("history_url")),
431-
visits: [
432-
{
433-
transition: lazy.PlacesUtils.history.TRANSITIONS.TYPED,
434-
date: parseNSDate(row.getResultByName("history_time")),
435-
},
436-
],
437-
});
428+
try {
429+
pageInfos.push({
430+
title: row.getResultByName("history_title"),
431+
url: new URL(row.getResultByName("history_url")),
432+
visits: [
433+
{
434+
transition: lazy.PlacesUtils.history.TRANSITIONS.TYPED,
435+
date: parseNSDate(row.getResultByName("history_time")),
436+
},
437+
],
438+
});
439+
} catch (e) {
440+
console.error("Could not create a history row: ", e);
441+
}
438442
}
439443
await MigrationUtils.insertVisitsWrapper(pageInfos);
440444

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/* Any copyright is dedicated to the Public Domain.
2+
http://creativecommons.org/publicdomain/zero/1.0/ */
3+
4+
"use strict";
5+
6+
const { PlacesQuery } = ChromeUtils.importESModule(
7+
"resource://gre/modules/PlacesQuery.sys.mjs"
8+
);
9+
10+
const HISTORY_FILE_PATH = "Library/Safari/History.db";
11+
const HISTORY_STRANGE_ENTRIES_FILE_PATH =
12+
"Library/Safari/HistoryStrangeEntries.db";
13+
14+
// By default, our migrators will cut off migrating any history older than
15+
// 180 days. In order to make sure this test continues to run correctly
16+
// in the future, we copy the reference database to History.db, and then
17+
// use Sqlite.sys.mjs to connect to it and manually update all of the visit
18+
// times to be "now", so that they all fall within the 180 day window. The
19+
// Nov 10th date below is right around when the reference database visit
20+
// entries were created.
21+
//
22+
// This update occurs in `updateVisitTimes`.
23+
const MS_SINCE_SNAPSHOT_TIME =
24+
new Date() - new Date("Nov 10, 2022 00:00:00 UTC");
25+
26+
async function setupHistoryFile() {
27+
removeHistoryFile();
28+
let file = do_get_file(HISTORY_STRANGE_ENTRIES_FILE_PATH);
29+
file.copyTo(file.parent, "History.db");
30+
await updateVisitTimes();
31+
}
32+
33+
function removeHistoryFile() {
34+
let file = do_get_file(HISTORY_FILE_PATH, true);
35+
try {
36+
file.remove(false);
37+
} catch (ex) {
38+
// It is ok if this doesn't exist.
39+
if (ex.result != Cr.NS_ERROR_FILE_NOT_FOUND) {
40+
throw ex;
41+
}
42+
}
43+
}
44+
45+
add_setup(async function setup() {
46+
registerFakePath("ULibDir", do_get_file("Library/"));
47+
await setupHistoryFile();
48+
registerCleanupFunction(async () => {
49+
await PlacesUtils.history.clear();
50+
removeHistoryFile();
51+
});
52+
});
53+
54+
async function updateVisitTimes() {
55+
let cocoaSnapshotDelta = MS_SINCE_SNAPSHOT_TIME / 1000;
56+
let historyFile = do_get_file(HISTORY_FILE_PATH);
57+
let dbConn = await Sqlite.openConnection({ path: historyFile.path });
58+
59+
await dbConn.execute(
60+
"UPDATE history_visits SET visit_time = visit_time + :cocoaSnapshotDelta;",
61+
{
62+
cocoaSnapshotDelta,
63+
}
64+
);
65+
66+
await dbConn.close();
67+
}
68+
69+
/**
70+
* Tests that we can import successfully from Safari when Safari's history
71+
* database contains malformed URLs.
72+
*/
73+
add_task(async function testHistoryImportStrangeEntries() {
74+
await PlacesUtils.history.clear();
75+
76+
let placesQuery = new PlacesQuery();
77+
let emptyHistory = await placesQuery.getHistory();
78+
Assert.equal(emptyHistory.size, 0, "Empty history should indeed be empty.");
79+
80+
const EXPECTED_MIGRATED_SITES = 10;
81+
const EXPECTED_MIGRATED_VISTS = 23;
82+
83+
let historyFile = do_get_file(HISTORY_FILE_PATH);
84+
let dbConn = await Sqlite.openConnection({ path: historyFile.path });
85+
let [rowCountResult] = await dbConn.execute(
86+
"SELECT COUNT(*) FROM history_visits"
87+
);
88+
ok(
89+
rowCountResult.getResultByName("COUNT(*)") > EXPECTED_MIGRATED_VISTS,
90+
"There are more total rows than valid rows"
91+
);
92+
await dbConn.close();
93+
94+
let migrator = await MigrationUtils.getMigrator("safari");
95+
await promiseMigration(migrator, MigrationUtils.resourceTypes.HISTORY);
96+
let migratedHistory = await placesQuery.getHistory({ sortBy: "site" });
97+
let siteCount = migratedHistory.size;
98+
let visitCount = 0;
99+
for (let [, visits] of migratedHistory) {
100+
visitCount += visits.length;
101+
}
102+
Assert.equal(
103+
siteCount,
104+
EXPECTED_MIGRATED_SITES,
105+
"Should have migrated all valid history sites"
106+
);
107+
Assert.equal(
108+
visitCount,
109+
EXPECTED_MIGRATED_VISTS,
110+
"Should have migrated all valid history visits"
111+
);
112+
113+
placesQuery.close();
114+
});

browser/components/migration/tests/unit/xpcshell.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ run-if = ["os == 'mac'"]
8383
["test_Safari_history.js"]
8484
run-if = ["os == 'mac'"]
8585

86+
["test_Safari_history_strange_entries.js"]
87+
run-if = ["os == 'mac'"]
88+
8689
["test_Safari_permissions.js"]
8790
run-if = ["os == 'mac'"]
8891

0 commit comments

Comments
 (0)