summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/tests/expiration/test_debug_expiration.js
blob: 204295d46ce19b061b4afef295aad651af43845a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

/**
 * What this is aimed to test:
 *
 * Expiration can be manually triggered through a debug topic, but that should
 * only expire orphan entries, unless -1 is passed as limit.
 */

const EXPIRE_DAYS = 90;
var gExpirableTime = getExpirablePRTime(EXPIRE_DAYS);
var gNonExpirableTime = getExpirablePRTime(EXPIRE_DAYS - 2);

add_task(async function test_expire_orphans() {
  // Add visits to 2 pages and force a orphan expiration. Visits should survive.
  await PlacesTestUtils.addVisits({
    uri: uri("http://page1.mozilla.org/"),
    visitDate: gExpirableTime++,
  });
  await PlacesTestUtils.addVisits({
    uri: uri("http://page2.mozilla.org/"),
    visitDate: gExpirableTime++,
  });
  // Create a orphan place.
  let bm = await PlacesUtils.bookmarks.insert({
    parentGuid: PlacesUtils.bookmarks.unfiledGuid,
    url: "http://page3.mozilla.org/",
    title: "",
  });
  await PlacesUtils.bookmarks.remove(bm);

  // Expire now.
  await promiseForceExpirationStep(0);

  // Check that visits survived.
  Assert.equal(visits_in_database("http://page1.mozilla.org/"), 1);
  Assert.equal(visits_in_database("http://page2.mozilla.org/"), 1);
  Assert.ok(!page_in_database("http://page3.mozilla.org/"));

  // Clean up.
  await PlacesUtils.history.clear();
});

add_task(async function test_expire_orphans_optionalarg() {
  // Add visits to 2 pages and force a orphan expiration. Visits should survive.
  await PlacesTestUtils.addVisits({
    uri: uri("http://page1.mozilla.org/"),
    visitDate: gExpirableTime++,
  });
  await PlacesTestUtils.addVisits({
    uri: uri("http://page2.mozilla.org/"),
    visitDate: gExpirableTime++,
  });
  // Create a orphan place.
  let bm = await PlacesUtils.bookmarks.insert({
    parentGuid: PlacesUtils.bookmarks.unfiledGuid,
    url: "http://page3.mozilla.org/",
    title: "",
  });
  await PlacesUtils.bookmarks.remove(bm);

  // Expire now.
  await promiseForceExpirationStep();

  // Check that visits survived.
  Assert.equal(visits_in_database("http://page1.mozilla.org/"), 1);
  Assert.equal(visits_in_database("http://page2.mozilla.org/"), 1);
  Assert.ok(!page_in_database("http://page3.mozilla.org/"));

  // Clean up.
  await PlacesUtils.history.clear();
});

add_task(async function test_expire_limited() {
  await PlacesTestUtils.addVisits([
    {
      // Should be expired cause it's the oldest visit
      uri: "http://old.mozilla.org/",
      visitDate: gExpirableTime++,
    },
    {
      // Should not be expired cause we limit 1
      uri: "http://new.mozilla.org/",
      visitDate: gExpirableTime++,
    },
  ]);

  // Expire now.
  await promiseForceExpirationStep(1);

  // Check that newer visit survived.
  Assert.equal(visits_in_database("http://new.mozilla.org/"), 1);
  // Other visits should have been expired.
  Assert.ok(!page_in_database("http://old.mozilla.org/"));

  // Clean up.
  await PlacesUtils.history.clear();
});

add_task(async function test_expire_visitcount_longurl() {
  let longurl = "http://long.mozilla.org/" + "a".repeat(232);
  let longurl2 = "http://long2.mozilla.org/" + "a".repeat(232);
  await PlacesTestUtils.addVisits([
    {
      // Should be expired cause it's the oldest visit
      uri: "http://old.mozilla.org/",
      visitDate: gExpirableTime++,
    },
    {
      // Should not be expired cause it has 2 visits.
      uri: longurl,
      visitDate: gExpirableTime++,
    },
    {
      uri: longurl,
      visitDate: gNonExpirableTime,
    },
    {
      // Should be expired cause it has 1 old visit.
      uri: longurl2,
      visitDate: gExpirableTime++,
    },
  ]);

  await promiseForceExpirationStep(1);

  // Check that some visits survived.
  Assert.equal(visits_in_database(longurl), 2);
  // Check visit has been removed.
  Assert.equal(visits_in_database(longurl2), 0);

  // Other visits should have been expired.
  Assert.ok(!page_in_database("http://old.mozilla.org/"));

  // Clean up.
  await PlacesUtils.history.clear();
});

add_task(async function test_expire_limited_exoticurl() {
  await PlacesTestUtils.addVisits([
    {
      // Should be expired cause it's the oldest visit
      uri: "http://old.mozilla.org/",
      visitDate: gExpirableTime++,
    },
    {
      // Should not be expired cause younger than EXPIRE_DAYS.
      uri: "http://nonexpirable-download.mozilla.org",
      visitDate: gNonExpirableTime,
      transition: PlacesUtils.history.TRANSITIONS.DOWNLOAD,
    },
    {
      // Should be expired cause it's a long url older than EXPIRE_DAYS.
      uri: "http://download.mozilla.org",
      visitDate: gExpirableTime++,
      transition: 7,
    },
  ]);

  await promiseForceExpirationStep(1);

  // Check that some visits survived.
  Assert.equal(
    visits_in_database("http://nonexpirable-download.mozilla.org/"),
    1
  );
  // The visits are gone, the url is not yet, cause we limited the expiration
  // to one entry, and we already removed http://old.mozilla.org/.
  // The page normally would be expired by the next expiration run.
  Assert.equal(visits_in_database("http://download.mozilla.org/"), 0);
  // Other visits should have been expired.
  Assert.ok(!page_in_database("http://old.mozilla.org/"));

  // Clean up.
  await PlacesUtils.history.clear();
});

add_task(async function test_expire_exotic_hidden() {
  let visits = [
    {
      // Should be expired cause it's the oldest visit
      uri: "http://old.mozilla.org/",
      visitDate: gExpirableTime++,
      expectedCount: 0,
    },
    {
      // Expirable typed hidden url.
      uri: "https://typedhidden.mozilla.org/",
      visitDate: gExpirableTime++,
      transition: PlacesUtils.history.TRANSITIONS.FRAMED_LINK,
      expectedCount: 2,
    },
    {
      // Mark as typed.
      uri: "https://typedhidden.mozilla.org/",
      visitDate: gExpirableTime++,
      transition: PlacesUtils.history.TRANSITIONS.TYPED,
      expectedCount: 2,
    },
    {
      // Expirable non-typed hidden url.
      uri: "https://hidden.mozilla.org/",
      visitDate: gExpirableTime++,
      transition: PlacesUtils.history.TRANSITIONS.FRAMED_LINK,
      expectedCount: 0,
    },
  ];
  await PlacesTestUtils.addVisits(visits);
  for (let visit of visits) {
    Assert.greater(visits_in_database(visit.uri), 0);
  }

  await promiseForceExpirationStep(1);

  for (let visit of visits) {
    Assert.equal(
      visits_in_database(visit.uri),
      visit.expectedCount,
      `${visit.uri} should${
        visit.expectedCount == 0 ? " " : " not "
      }have been expired`
    );
  }
  // Clean up.
  await PlacesUtils.history.clear();
});

add_task(async function test_expire_unlimited() {
  let longurl = "http://long.mozilla.org/" + "a".repeat(232);
  await PlacesTestUtils.addVisits([
    {
      uri: "http://old.mozilla.org/",
      visitDate: gExpirableTime++,
    },
    {
      uri: "http://new.mozilla.org/",
      visitDate: gExpirableTime++,
    },
    // Add expirable visits.
    {
      uri: "http://download.mozilla.org/",
      visitDate: gExpirableTime++,
      transition: PlacesUtils.history.TRANSITION_DOWNLOAD,
    },
    {
      uri: longurl,
      visitDate: gExpirableTime++,
    },

    // Add non-expirable visits
    {
      uri: "http://nonexpirable.mozilla.org/",
      visitDate: getExpirablePRTime(5),
    },
    {
      uri: "http://nonexpirable-download.mozilla.org/",
      visitDate: getExpirablePRTime(5),
      transition: PlacesUtils.history.TRANSITION_DOWNLOAD,
    },
    {
      uri: longurl,
      visitDate: getExpirablePRTime(5),
    },
  ]);

  await promiseForceExpirationStep(-1);

  // Check that some visits survived.
  Assert.equal(visits_in_database("http://nonexpirable.mozilla.org/"), 1);
  Assert.equal(
    visits_in_database("http://nonexpirable-download.mozilla.org/"),
    1
  );
  Assert.equal(visits_in_database(longurl), 1);
  // Other visits should have been expired.
  Assert.ok(!page_in_database("http://old.mozilla.org/"));
  Assert.ok(!page_in_database("http://download.mozilla.org/"));
  Assert.ok(!page_in_database("http://new.mozilla.org/"));

  // Clean up.
  await PlacesUtils.history.clear();
});

add_task(async function test_expire_icons() {
  const dataUrl =
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAA" +
    "AAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";

  const entries = [
    {
      desc: "Not expired because recent",
      page: "https://recent.notexpired.org/",
      icon: "https://recent.notexpired.org/test_icon.png",
      root: "https://recent.notexpired.org/favicon.ico",
      iconExpired: false,
      removed: false,
    },
    {
      desc: "Not expired because recent, no root",
      page: "https://recentnoroot.notexpired.org/",
      icon: "https://recentnoroot.notexpired.org/test_icon.png",
      iconExpired: false,
      removed: false,
    },
    {
      desc: "Expired because old with root",
      page: "https://oldroot.expired.org/",
      icon: "https://oldroot.expired.org/test_icon.png",
      root: "https://oldroot.expired.org/favicon.ico",
      iconExpired: true,
      removed: true,
    },
    {
      desc: "Not expired because bookmarked, even if old with root",
      page: "https://oldrootbm.notexpired.org/",
      icon: "https://oldrootbm.notexpired.org/test_icon.png",
      root: "https://oldrootbm.notexpired.org/favicon.ico",
      bookmarked: true,
      iconExpired: true,
      removed: false,
    },
    {
      desc: "Not Expired because old but has no root",
      page: "https://old.notexpired.org/",
      icon: "https://old.notexpired.org/test_icon.png",
      iconExpired: true,
      removed: false,
    },
    {
      desc: "Expired because it's an orphan page",
      page: "http://root.ref.org/#test",
      icon: undefined,
      iconExpired: false,
      removed: true,
    },
    {
      desc: "Expired because it's an orphan page",
      page: "http://root.ref.org/#test",
      icon: undefined,
      skipHistory: true,
      iconExpired: false,
      removed: true,
    },
  ];

  for (let entry of entries) {
    if (!entry.skipHistory) {
      await PlacesTestUtils.addVisits(entry.page);
    }
    if (entry.bookmarked) {
      await PlacesUtils.bookmarks.insert({
        url: entry.page,
        parentGuid: PlacesUtils.bookmarks.unfiledGuid,
      });
    }

    if (entry.icon) {
      PlacesUtils.favicons.replaceFaviconDataFromDataURL(
        Services.io.newURI(entry.icon),
        dataUrl,
        0,
        Services.scriptSecurityManager.getSystemPrincipal()
      );
      await PlacesTestUtils.addFavicons(new Map([[entry.page, entry.icon]]));
      Assert.equal(
        await getFaviconUrlForPage(entry.page),
        entry.icon,
        "Sanity check the icon exists"
      );
    } else {
      // This is an orphan page entry.
      await PlacesUtils.withConnectionWrapper("addOrphanPage", async db => {
        await db.execute(
          `INSERT INTO moz_pages_w_icons (page_url, page_url_hash)
           VALUES (:url, hash(:url))`,
          { url: entry.page }
        );
      });
    }

    if (entry.root) {
      PlacesUtils.favicons.replaceFaviconDataFromDataURL(
        Services.io.newURI(entry.root),
        dataUrl,
        0,
        Services.scriptSecurityManager.getSystemPrincipal()
      );
      await PlacesTestUtils.addFavicons(new Map([[entry.page, entry.root]]));
    }

    if (entry.iconExpired) {
      // Set an expired time on the icon.
      await PlacesUtils.withConnectionWrapper("expireFavicon", async db => {
        await db.execute(
          `UPDATE moz_icons_to_pages SET expire_ms = 1
           WHERE icon_id = (SELECT id FROM moz_icons WHERE icon_url = :url)`,
          { url: entry.icon }
        );
        if (entry.root) {
          await db.execute(
            `UPDATE moz_icons SET expire_ms = 1 WHERE icon_url = :url`,
            { url: entry.root }
          );
        }
      });
    }
    if (entry.icon) {
      Assert.equal(
        await getFaviconUrlForPage(entry.page),
        entry.icon,
        "Sanity check the initial icon value"
      );
    }
  }

  info("Run expiration");
  await promiseForceExpirationStep(-1);

  info("Check expiration");
  for (let entry of entries) {
    Assert.ok(page_in_database(entry.page));

    if (!entry.removed) {
      Assert.equal(
        await getFaviconUrlForPage(entry.page),
        entry.icon,
        entry.desc
      );
      continue;
    }

    if (entry.root) {
      Assert.equal(
        await getFaviconUrlForPage(entry.page),
        entry.root,
        entry.desc
      );
      continue;
    }

    if (entry.icon) {
      await Assert.rejects(
        getFaviconUrlForPage(entry.page),
        /Unable to find an icon/,
        entry.desc
      );
      continue;
    }

    // This was an orphan page entry.
    let db = await PlacesUtils.promiseDBConnection();
    let rows = await db.execute(
      `SELECT count(*) FROM moz_pages_w_icons WHERE page_url_hash = hash(:url)`,
      { url: entry.page }
    );
    Assert.equal(rows[0].getResultByIndex(0), 0, "Orphan page was removed");
  }

  // Clean up.
  await PlacesUtils.history.clear();
});

add_setup(async function () {
  // Set interval to a large value so we don't expire on it.
  setInterval(3600); // 1h
  // Set maxPages to a low value, so it's easy to go over it.
  setMaxPages(1);
});