summaryrefslogtreecommitdiffstats
path: root/browser/components/places/tests/unit
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /browser/components/places/tests/unit
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/places/tests/unit')
-rw-r--r--browser/components/places/tests/unit/bookmarks.glue.html16
-rw-r--r--browser/components/places/tests/unit/bookmarks.glue.json83
-rw-r--r--browser/components/places/tests/unit/corruptDB.sqlitebin0 -> 32772 bytes
-rw-r--r--browser/components/places/tests/unit/distribution.ini30
-rw-r--r--browser/components/places/tests/unit/head_bookmarks.js82
-rw-r--r--browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js107
-rw-r--r--browser/components/places/tests/unit/test_PUIU_setCharsetForPage.js141
-rw-r--r--browser/components/places/tests/unit/test_PUIU_title_difference_spotter.js81
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_bookmarkshtml.js33
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_corrupt.js53
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js47
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js50
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_distribution.js143
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_migrate.js68
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_prefs.js161
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_restore.js55
-rw-r--r--browser/components/places/tests/unit/test_clearHistory_shutdown.js183
-rw-r--r--browser/components/places/tests/unit/test_interactions_blocklist.js67
-rw-r--r--browser/components/places/tests/unit/test_invalid_defaultLocation.js24
-rw-r--r--browser/components/places/tests/unit/xpcshell.toml38
20 files changed, 1462 insertions, 0 deletions
diff --git a/browser/components/places/tests/unit/bookmarks.glue.html b/browser/components/places/tests/unit/bookmarks.glue.html
new file mode 100644
index 0000000000..07b22e9b3f
--- /dev/null
+++ b/browser/components/places/tests/unit/bookmarks.glue.html
@@ -0,0 +1,16 @@
+<!DOCTYPE NETSCAPE-Bookmark-file-1>
+<!-- This is an automatically generated file.
+ It will be read and overwritten.
+ DO NOT EDIT! -->
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
+<TITLE>Bookmarks</TITLE>
+<H1>Bookmarks Menu</H1>
+
+<DL><p>
+ <DT><A HREF="http://example.com/" ADD_DATE="1233157972" LAST_MODIFIED="1233157984">example</A>
+ <DT><H3 ADD_DATE="1233157910" LAST_MODIFIED="1233157972" PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar</H3>
+<DD>Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar
+ <DL><p>
+ <DT><A HREF="http://example.com/" ADD_DATE="1233157972" LAST_MODIFIED="1233157984">example</A>
+ </DL><p>
+</DL><p>
diff --git a/browser/components/places/tests/unit/bookmarks.glue.json b/browser/components/places/tests/unit/bookmarks.glue.json
new file mode 100644
index 0000000000..069f605d29
--- /dev/null
+++ b/browser/components/places/tests/unit/bookmarks.glue.json
@@ -0,0 +1,83 @@
+{
+ "title": "",
+ "id": 1,
+ "dateAdded": 1233157910552624,
+ "lastModified": 1233157955206833,
+ "type": "text/x-moz-place-container",
+ "root": "placesRoot",
+ "children": [
+ {
+ "title": "Bookmarks Menu",
+ "id": 2,
+ "parent": 1,
+ "dateAdded": 1233157910552624,
+ "lastModified": 1233157993171424,
+ "type": "text/x-moz-place-container",
+ "root": "bookmarksMenuFolder",
+ "children": [
+ {
+ "title": "examplejson",
+ "id": 27,
+ "parent": 2,
+ "dateAdded": 1233157972101126,
+ "lastModified": 1233157984999673,
+ "type": "text/x-moz-place",
+ "uri": "http://example.com/"
+ }
+ ]
+ },
+ {
+ "index": 1,
+ "title": "Bookmarks Toolbar",
+ "id": 3,
+ "parent": 1,
+ "dateAdded": 1233157910552624,
+ "lastModified": 1233157972101126,
+ "annos": [
+ {
+ "name": "bookmarkProperties/description",
+ "flags": 0,
+ "expires": 4,
+ "mimeType": null,
+ "type": 3,
+ "value": "Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar"
+ }
+ ],
+ "type": "text/x-moz-place-container",
+ "root": "toolbarFolder",
+ "children": [
+ {
+ "title": "examplejson",
+ "id": 26,
+ "parent": 3,
+ "dateAdded": 1233157972101126,
+ "lastModified": 1233157984999673,
+ "type": "text/x-moz-place",
+ "uri": "http://example.com/"
+ }
+ ]
+ },
+ {
+ "index": 2,
+ "title": "Tags",
+ "id": 4,
+ "parent": 1,
+ "dateAdded": 1233157910552624,
+ "lastModified": 1233157910582667,
+ "type": "text/x-moz-place-container",
+ "root": "tagsFolder",
+ "children": []
+ },
+ {
+ "index": 3,
+ "title": "Other Bookmarks",
+ "id": 5,
+ "parent": 1,
+ "dateAdded": 1233157910552624,
+ "lastModified": 1233157911033315,
+ "type": "text/x-moz-place-container",
+ "root": "unfiledBookmarksFolder",
+ "children": []
+ }
+ ]
+}
diff --git a/browser/components/places/tests/unit/corruptDB.sqlite b/browser/components/places/tests/unit/corruptDB.sqlite
new file mode 100644
index 0000000000..b234246cac
--- /dev/null
+++ b/browser/components/places/tests/unit/corruptDB.sqlite
Binary files differ
diff --git a/browser/components/places/tests/unit/distribution.ini b/browser/components/places/tests/unit/distribution.ini
new file mode 100644
index 0000000000..a25c40fed3
--- /dev/null
+++ b/browser/components/places/tests/unit/distribution.ini
@@ -0,0 +1,30 @@
+# Distribution Configuration File
+# Bug 516444 demo
+
+[Global]
+id=516444
+version=1.0
+about=Test distribution file
+
+[BookmarksToolbar]
+item.1.title=Toolbar Link Before
+item.1.link=https://example.org/toolbar/before/
+item.1.icon=https://example.org/favicon.png
+item.1.iconData=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==
+item.2.type=default
+item.3.type=folder
+item.3.title=Toolbar Folder After
+item.3.folderId=1
+
+[BookmarksMenu]
+item.1.title=Menu Link Before
+item.1.link=https://example.org/menu/before/
+item.1.icon=https://example.org/favicon.png
+item.1.iconData=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==
+item.2.type=default
+item.3.title=Menu Link After
+item.3.link=https://example.org/menu/after/
+
+[BookmarksFolder-1]
+item.1.title=Toolbar Link Folder
+item.1.link=https://example.org/toolbar/folder/
diff --git a/browser/components/places/tests/unit/head_bookmarks.js b/browser/components/places/tests/unit/head_bookmarks.js
new file mode 100644
index 0000000000..db6bfe8f0f
--- /dev/null
+++ b/browser/components/places/tests/unit/head_bookmarks.js
@@ -0,0 +1,82 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Import common head.
+/* import-globals-from ../../../../../toolkit/components/places/tests/head_common.js */
+var commonFile = do_get_file(
+ "../../../../../toolkit/components/places/tests/head_common.js",
+ false
+);
+if (commonFile) {
+ let uri = Services.io.newFileURI(commonFile);
+ Services.scriptloader.loadSubScript(uri.spec, this);
+}
+
+// Put any other stuff relative to this test folder below.
+
+ChromeUtils.defineESModuleGetters(this, {
+ PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs",
+});
+
+// Needed by some test that relies on having an app registered.
+const { updateAppInfo } = ChromeUtils.importESModule(
+ "resource://testing-common/AppInfo.sys.mjs"
+);
+updateAppInfo({
+ name: "PlacesTest",
+ ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
+ version: "1",
+ platformVersion: "",
+});
+
+// Default bookmarks constants.
+const DEFAULT_BOOKMARKS_ON_TOOLBAR = 1;
+const DEFAULT_BOOKMARKS_ON_MENU = 1;
+
+var createCorruptDB = async function () {
+ let dbPath = PathUtils.join(PathUtils.profileDir, "places.sqlite");
+ await IOUtils.remove(dbPath);
+
+ // Create a corrupt database.
+ let src = PathUtils.join(do_get_cwd().path, "corruptDB.sqlite");
+ await IOUtils.copy(src, dbPath);
+
+ // Check there's a DB now.
+ Assert.ok(await IOUtils.exists(dbPath), "should have a DB now");
+};
+
+const SINGLE_TRY_TIMEOUT = 100;
+const NUMBER_OF_TRIES = 30;
+
+/**
+ * Similar to waitForConditionPromise, but poll for an asynchronous value
+ * every SINGLE_TRY_TIMEOUT ms, for no more than tryCount times.
+ *
+ * @param {Function} promiseFn
+ * A function to generate a promise, which resolves to the expected
+ * asynchronous value.
+ * @param {msg} timeoutMsg
+ * The reason to reject the returned promise with.
+ * @param {number} [tryCount]
+ * Maximum times to try before rejecting the returned promise with
+ * timeoutMsg, defaults to NUMBER_OF_TRIES.
+ * @returns {Promise} to the asynchronous value being polled.
+ * @throws if the asynchronous value is not available after tryCount attempts.
+ */
+var waitForResolvedPromise = async function (
+ promiseFn,
+ timeoutMsg,
+ tryCount = NUMBER_OF_TRIES
+) {
+ let tries = 0;
+ do {
+ try {
+ let value = await promiseFn();
+ return value;
+ } catch (ex) {}
+ await new Promise(resolve => do_timeout(SINGLE_TRY_TIMEOUT, resolve));
+ } while (++tries <= tryCount);
+ throw new Error(timeoutMsg);
+};
diff --git a/browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js b/browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js
new file mode 100644
index 0000000000..58b68d7574
--- /dev/null
+++ b/browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js
@@ -0,0 +1,107 @@
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+/* eslint-disable mozilla/use-chromeutils-generateqi */
+
+add_task(async function test_no_result_node() {
+ let functionSpy = sinon.stub().returns(Promise.resolve());
+
+ await PlacesUIUtils.batchUpdatesForNode(null, 1, functionSpy);
+
+ Assert.ok(
+ functionSpy.calledOnce,
+ "Passing a null result node should still call the wrapped function"
+ );
+});
+
+add_task(async function test_under_batch_threshold() {
+ let functionSpy = sinon.stub().returns(Promise.resolve());
+ let resultNode = {
+ QueryInterface() {
+ return this;
+ },
+ onBeginUpdateBatch: sinon.spy(),
+ onEndUpdateBatch: sinon.spy(),
+ };
+
+ await PlacesUIUtils.batchUpdatesForNode(resultNode, 1, functionSpy);
+
+ Assert.ok(functionSpy.calledOnce, "Wrapped function should be called once");
+ Assert.ok(
+ resultNode.onBeginUpdateBatch.notCalled,
+ "onBeginUpdateBatch should not have been called"
+ );
+ Assert.ok(
+ resultNode.onEndUpdateBatch.notCalled,
+ "onEndUpdateBatch should not have been called"
+ );
+});
+
+add_task(async function test_over_batch_threshold() {
+ let functionSpy = sinon.stub().callsFake(() => {
+ Assert.ok(
+ resultNode.onBeginUpdateBatch.calledOnce,
+ "onBeginUpdateBatch should have been called before the function"
+ );
+ Assert.ok(
+ resultNode.onEndUpdateBatch.notCalled,
+ "onEndUpdateBatch should not have been called before the function"
+ );
+
+ return Promise.resolve();
+ });
+ let resultNode = {
+ QueryInterface() {
+ return this;
+ },
+ onBeginUpdateBatch: sinon.spy(),
+ onEndUpdateBatch: sinon.spy(),
+ };
+
+ await PlacesUIUtils.batchUpdatesForNode(resultNode, 100, functionSpy);
+
+ Assert.ok(functionSpy.calledOnce, "Wrapped function should be called once");
+ Assert.ok(
+ resultNode.onBeginUpdateBatch.calledOnce,
+ "onBeginUpdateBatch should have been called"
+ );
+ Assert.ok(
+ resultNode.onEndUpdateBatch.calledOnce,
+ "onEndUpdateBatch should have been called"
+ );
+});
+
+add_task(async function test_wrapped_function_throws() {
+ let error = new Error("Failed!");
+ let functionSpy = sinon.stub().throws(error);
+ let resultNode = {
+ QueryInterface() {
+ return this;
+ },
+ onBeginUpdateBatch: sinon.spy(),
+ onEndUpdateBatch: sinon.spy(),
+ };
+
+ let raisedError;
+ try {
+ await PlacesUIUtils.batchUpdatesForNode(resultNode, 100, functionSpy);
+ } catch (ex) {
+ raisedError = ex;
+ }
+
+ Assert.ok(functionSpy.calledOnce, "Wrapped function should be called once");
+ Assert.ok(
+ resultNode.onBeginUpdateBatch.calledOnce,
+ "onBeginUpdateBatch should have been called"
+ );
+ Assert.ok(
+ resultNode.onEndUpdateBatch.calledOnce,
+ "onEndUpdateBatch should have been called"
+ );
+ Assert.equal(
+ raisedError,
+ error,
+ "batchUpdatesForNode should have raised the error from the wrapped function"
+ );
+});
diff --git a/browser/components/places/tests/unit/test_PUIU_setCharsetForPage.js b/browser/components/places/tests/unit/test_PUIU_setCharsetForPage.js
new file mode 100644
index 0000000000..db213971e9
--- /dev/null
+++ b/browser/components/places/tests/unit/test_PUIU_setCharsetForPage.js
@@ -0,0 +1,141 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { PrivateBrowsingUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/PrivateBrowsingUtils.sys.mjs"
+);
+
+const UTF8 = "UTF-8";
+const UTF16 = "UTF-16";
+
+const TEST_URI = "http://foo.com";
+const TEST_BOOKMARKED_URI = "http://bar.com";
+
+add_task(function setup() {
+ let savedIsWindowPrivateFunc = PrivateBrowsingUtils.isWindowPrivate;
+ PrivateBrowsingUtils.isWindowPrivate = () => false;
+
+ registerCleanupFunction(() => {
+ PrivateBrowsingUtils.isWindowPrivate = savedIsWindowPrivateFunc;
+ });
+});
+
+add_task(async function test_simple_add() {
+ // add pages to history
+ await PlacesTestUtils.addVisits(TEST_URI);
+ await PlacesTestUtils.addVisits(TEST_BOOKMARKED_URI);
+
+ // create bookmarks on TEST_BOOKMARKED_URI
+ await PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ url: TEST_BOOKMARKED_URI,
+ title: TEST_BOOKMARKED_URI.spec,
+ });
+ await PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ url: TEST_BOOKMARKED_URI,
+ title: TEST_BOOKMARKED_URI.spec,
+ });
+
+ // set charset on not-bookmarked page
+ await PlacesUIUtils.setCharsetForPage(TEST_URI, UTF16, {});
+ // set charset on bookmarked page
+ await PlacesUIUtils.setCharsetForPage(TEST_BOOKMARKED_URI, UTF16, {});
+
+ let pageInfo = await PlacesUtils.history.fetch(TEST_URI, {
+ includeAnnotations: true,
+ });
+ Assert.equal(
+ pageInfo.annotations.get(PlacesUtils.CHARSET_ANNO),
+ UTF16,
+ "Should return correct charset for a not-bookmarked page"
+ );
+
+ pageInfo = await PlacesUtils.history.fetch(TEST_BOOKMARKED_URI, {
+ includeAnnotations: true,
+ });
+ Assert.equal(
+ pageInfo.annotations.get(PlacesUtils.CHARSET_ANNO),
+ UTF16,
+ "Should return correct charset for a bookmarked page"
+ );
+
+ await PlacesUtils.history.clear();
+
+ pageInfo = await PlacesUtils.history.fetch(TEST_URI, {
+ includeAnnotations: true,
+ });
+ Assert.ok(
+ !pageInfo,
+ "Should not return pageInfo for a page after history cleared"
+ );
+
+ pageInfo = await PlacesUtils.history.fetch(TEST_BOOKMARKED_URI, {
+ includeAnnotations: true,
+ });
+ Assert.equal(
+ pageInfo.annotations.get(PlacesUtils.CHARSET_ANNO),
+ UTF16,
+ "Charset should still be set for a bookmarked page after history clear"
+ );
+
+ await PlacesUIUtils.setCharsetForPage(TEST_BOOKMARKED_URI, "");
+ pageInfo = await PlacesUtils.history.fetch(TEST_BOOKMARKED_URI, {
+ includeAnnotations: true,
+ });
+ Assert.strictEqual(
+ pageInfo.annotations.get(PlacesUtils.CHARSET_ANNO),
+ undefined,
+ "Should not have a charset after it has been removed from the page"
+ );
+});
+
+add_task(async function test_utf8_clears_saved_anno() {
+ await PlacesUtils.history.clear();
+ await PlacesTestUtils.addVisits(TEST_URI);
+
+ // set charset on bookmarked page
+ await PlacesUIUtils.setCharsetForPage(TEST_URI, UTF16, {});
+
+ let pageInfo = await PlacesUtils.history.fetch(TEST_URI, {
+ includeAnnotations: true,
+ });
+ Assert.equal(
+ pageInfo.annotations.get(PlacesUtils.CHARSET_ANNO),
+ UTF16,
+ "Should return correct charset for a not-bookmarked page"
+ );
+
+ // Now set the bookmark to a UTF-8 charset.
+ await PlacesUIUtils.setCharsetForPage(TEST_URI, UTF8, {});
+
+ pageInfo = await PlacesUtils.history.fetch(TEST_URI, {
+ includeAnnotations: true,
+ });
+ Assert.strictEqual(
+ pageInfo.annotations.get(PlacesUtils.CHARSET_ANNO),
+ undefined,
+ "Should have removed the charset for a UTF-8 page."
+ );
+});
+
+add_task(async function test_private_browsing_not_saved() {
+ await PlacesUtils.history.clear();
+ await PlacesTestUtils.addVisits(TEST_URI);
+
+ // set charset on bookmarked page, but pretend this is a private browsing window.
+ PrivateBrowsingUtils.isWindowPrivate = () => true;
+ await PlacesUIUtils.setCharsetForPage(TEST_URI, UTF16, {});
+
+ let pageInfo = await PlacesUtils.history.fetch(TEST_URI, {
+ includeAnnotations: true,
+ });
+ Assert.strictEqual(
+ pageInfo.annotations.get(PlacesUtils.CHARSET_ANNO),
+ undefined,
+ "Should not have set the charset in a private browsing window."
+ );
+});
diff --git a/browser/components/places/tests/unit/test_PUIU_title_difference_spotter.js b/browser/components/places/tests/unit/test_PUIU_title_difference_spotter.js
new file mode 100644
index 0000000000..aaa4db6bb2
--- /dev/null
+++ b/browser/components/places/tests/unit/test_PUIU_title_difference_spotter.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests the difference indication for titles.
+ */
+
+const TESTS = [
+ {
+ title: null,
+ expected: undefined,
+ },
+ {
+ title: "Short title",
+ expected: undefined,
+ },
+ {
+ title: "Short title2",
+ expected: undefined,
+ },
+ {
+ title: "Long title same as another",
+ expected: undefined,
+ },
+ {
+ title: "Long title same as another",
+ expected: undefined,
+ },
+ {
+ title: "Long title with difference at the end 1",
+ expected: 38,
+ },
+ {
+ title: "Long title with difference at the end 2",
+ expected: 38,
+ },
+ {
+ title: "A long title with difference 123456 in the middle.",
+ expected: 30,
+ },
+ {
+ title: "A long title with difference 135246 in the middle.",
+ expected: 30,
+ },
+ {
+ title:
+ "Some long titles with variable 12345678 differences to 13572468 other titles",
+ expected: 32,
+ },
+ {
+ title:
+ "Some long titles with variable 12345678 differences to 15263748 other titles",
+ expected: 32,
+ },
+ {
+ title:
+ "Some long titles with variable 15263748 differences to 12345678 other titles",
+ expected: 32,
+ },
+ {
+ title: "One long title which will be shorter than the other one",
+ expected: 40,
+ },
+ {
+ title:
+ "One long title which will be shorter that the other one (not this one)",
+ expected: 40,
+ },
+];
+
+add_task(async function test_difference_finding() {
+ PlacesUIUtils.insertTitleStartDiffs(TESTS);
+
+ for (let result of TESTS) {
+ Assert.equal(
+ result.titleDifferentIndex,
+ result.expected,
+ `Should have returned the correct index for "${result.title}"`
+ );
+ }
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_bookmarkshtml.js b/browser/components/places/tests/unit/test_browserGlue_bookmarkshtml.js
new file mode 100644
index 0000000000..65a038487a
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_bookmarkshtml.js
@@ -0,0 +1,33 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Tests that nsBrowserGlue correctly exports bookmarks.html at shutdown if
+ * browser.bookmarks.autoExportHTML is set to true.
+ */
+
+add_task(async function () {
+ remove_bookmarks_html();
+
+ Services.prefs.setBoolPref("browser.bookmarks.autoExportHTML", true);
+ registerCleanupFunction(() =>
+ Services.prefs.clearUserPref("browser.bookmarks.autoExportHTML")
+ );
+
+ // Initialize nsBrowserGlue before Places.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsISupports);
+
+ // Initialize Places through the History Service.
+ Assert.equal(
+ PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CREATE
+ );
+
+ Services.obs.addObserver(function observer() {
+ Services.obs.removeObserver(observer, "profile-before-change");
+ check_bookmarks_html();
+ }, "profile-before-change");
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_corrupt.js b/browser/components/places/tests/unit/test_browserGlue_corrupt.js
new file mode 100644
index 0000000000..0514b06bbc
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_corrupt.js
@@ -0,0 +1,53 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Tests that nsBrowserGlue correctly restores bookmarks from a JSON backup if
+ * database is corrupt and one backup is available.
+ */
+
+function run_test() {
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ remove_all_JSON_backups();
+
+ // Create our JSON backup from bookmarks.glue.json.
+ create_JSON_backup("bookmarks.glue.json");
+
+ run_next_test();
+}
+
+registerCleanupFunction(function () {
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+ return PlacesUtils.bookmarks.eraseEverything();
+});
+
+add_task(async function test_main() {
+ // Create a corrupt database.
+ await createCorruptDB();
+
+ // Initialize nsBrowserGlue before Places.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsISupports);
+
+ // Check the database was corrupt.
+ // nsBrowserGlue uses databaseStatus to manage initialization.
+ Assert.equal(
+ PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CORRUPT
+ );
+
+ // The test will continue once restore has finished.
+ await promiseTopicObserved("places-browser-init-complete");
+
+ // Check that JSON backup has been restored.
+ let bm = await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ });
+ Assert.equal(bm.title, "examplejson");
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js b/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js
new file mode 100644
index 0000000000..a0bc93c74c
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js
@@ -0,0 +1,47 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Tests that nsBrowserGlue correctly imports from bookmarks.html if database
+ * is corrupt but a JSON backup is not available.
+ */
+
+function run_test() {
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ // Remove JSON backup from profile.
+ remove_all_JSON_backups();
+
+ run_next_test();
+}
+
+registerCleanupFunction(remove_bookmarks_html);
+
+add_task(async function () {
+ // Create a corrupt database.
+ await createCorruptDB();
+
+ // Initialize nsBrowserGlue before Places.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsISupports);
+
+ // Check the database was corrupt.
+ // nsBrowserGlue uses databaseStatus to manage initialization.
+ Assert.equal(
+ PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CORRUPT
+ );
+
+ // The test will continue once import has finished.
+ await promiseTopicObserved("places-browser-init-complete");
+
+ // Check that bookmarks html has been restored.
+ let bm = await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ });
+ Assert.equal(bm.title, "example");
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js b/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js
new file mode 100644
index 0000000000..031d82d9e2
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js
@@ -0,0 +1,50 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Tests that nsBrowserGlue correctly restores default bookmarks if database is
+ * corrupt, nor a JSON backup nor bookmarks.html are available.
+ */
+
+function run_test() {
+ // Remove bookmarks.html from profile.
+ remove_bookmarks_html();
+
+ // Remove JSON backup from profile.
+ remove_all_JSON_backups();
+
+ run_next_test();
+}
+
+add_task(async function () {
+ // Create a corrupt database.
+ await createCorruptDB();
+
+ // Initialize nsBrowserGlue before Places.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsISupports);
+
+ // Check the database was corrupt.
+ // nsBrowserGlue uses databaseStatus to manage initialization.
+ Assert.equal(
+ PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CORRUPT
+ );
+
+ // The test will continue once import has finished.
+ await promiseTopicObserved("places-browser-init-complete");
+
+ // Check that default bookmarks have been restored.
+ let bm = await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ });
+
+ // Bug 1283076: Nightly bookmark points to Get Involved page, not Getting Started one
+ let chanTitle = AppConstants.NIGHTLY_BUILD
+ ? "Get Involved"
+ : "Getting Started";
+ Assert.equal(bm.title, chanTitle);
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_distribution.js b/browser/components/places/tests/unit/test_browserGlue_distribution.js
new file mode 100644
index 0000000000..5fafba457f
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_distribution.js
@@ -0,0 +1,143 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that nsBrowserGlue correctly imports bookmarks from distribution.ini.
+ */
+
+const PREF_BMPROCESSED = "distribution.516444.bookmarksProcessed";
+const PREF_DISTRIBUTION_ID = "distribution.id";
+
+const TOPICDATA_DISTRIBUTION_CUSTOMIZATION = "force-distribution-customization";
+const TOPIC_CUSTOMIZATION_COMPLETE = "distribution-customization-complete";
+const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
+
+function run_test() {
+ // Set special pref to load distribution.ini from the profile folder.
+ Services.prefs.setBoolPref("distribution.testing.loadFromProfile", true);
+
+ // Copy distribution.ini file to the profile dir.
+ let distroDir = gProfD.clone();
+ distroDir.leafName = "distribution";
+ let iniFile = distroDir.clone();
+ iniFile.append("distribution.ini");
+ if (iniFile.exists()) {
+ iniFile.remove(false);
+ print("distribution.ini already exists, did some test forget to cleanup?");
+ }
+
+ let testDistributionFile = gTestDir.clone();
+ testDistributionFile.append("distribution.ini");
+ testDistributionFile.copyTo(distroDir, "distribution.ini");
+ Assert.ok(testDistributionFile.exists());
+
+ run_next_test();
+}
+
+registerCleanupFunction(function () {
+ // Remove the distribution file, even if the test failed, otherwise all
+ // next tests will import it.
+ let iniFile = gProfD.clone();
+ iniFile.leafName = "distribution";
+ iniFile.append("distribution.ini");
+ if (iniFile.exists()) {
+ iniFile.remove(false);
+ }
+ Assert.ok(!iniFile.exists());
+});
+
+add_task(async function () {
+ let { DistributionCustomizer } = ChromeUtils.importESModule(
+ "resource:///modules/distribution.sys.mjs"
+ );
+ let distribution = new DistributionCustomizer();
+
+ let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(
+ Ci.nsIObserver
+ );
+ // Initialize Places through the History Service and check that a new
+ // database has been created.
+ Assert.equal(
+ PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CREATE
+ );
+ // Force distribution.
+ glue.observe(
+ null,
+ TOPIC_BROWSERGLUE_TEST,
+ TOPICDATA_DISTRIBUTION_CUSTOMIZATION
+ );
+
+ // Test will continue on customization complete notification.
+ await promiseTopicObserved(TOPIC_CUSTOMIZATION_COMPLETE);
+
+ // Check the custom bookmarks exist on menu.
+ let menuItem = await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: 0,
+ });
+ Assert.equal(menuItem.title, "Menu Link Before");
+ Assert.ok(
+ menuItem.guid.startsWith(distribution.BOOKMARK_GUID_PREFIX),
+ "Guid of this bookmark has expected prefix"
+ );
+
+ menuItem = await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: 1 + DEFAULT_BOOKMARKS_ON_MENU,
+ });
+ Assert.equal(menuItem.title, "Menu Link After");
+
+ // Check no favicon exists for this bookmark
+ await Assert.rejects(
+ waitForResolvedPromise(
+ () => {
+ return PlacesUtils.promiseFaviconData(menuItem.url.href);
+ },
+ "Favicon not found",
+ 10
+ ),
+ /Favicon\snot\sfound/,
+ "Favicon not found"
+ );
+
+ // Check the custom bookmarks exist on toolbar.
+ let toolbarItem = await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ });
+ Assert.equal(toolbarItem.title, "Toolbar Link Before");
+
+ // Check the custom favicon exist for this bookmark
+ let faviconItem = await waitForResolvedPromise(
+ () => {
+ return PlacesUtils.promiseFaviconData(toolbarItem.url.href);
+ },
+ "Favicon not found",
+ 10
+ );
+ Assert.equal(faviconItem.uri.spec, "https://example.org/favicon.png");
+ Assert.greater(faviconItem.dataLen, 0);
+ Assert.equal(faviconItem.mimeType, "image/png");
+
+ let base64Icon =
+ "data:image/png;base64," +
+ base64EncodeString(String.fromCharCode.apply(String, faviconItem.data));
+ Assert.equal(base64Icon, SMALLPNG_DATA_URI.spec);
+
+ toolbarItem = await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 1 + DEFAULT_BOOKMARKS_ON_TOOLBAR,
+ });
+ Assert.equal(toolbarItem.title, "Toolbar Folder After");
+ Assert.ok(
+ toolbarItem.guid.startsWith(distribution.FOLDER_GUID_PREFIX),
+ "Guid of this folder has expected prefix"
+ );
+
+ // Check the bmprocessed pref has been created.
+ Assert.ok(Services.prefs.getBoolPref(PREF_BMPROCESSED));
+
+ // Check distribution prefs have been created.
+ Assert.equal(Services.prefs.getCharPref(PREF_DISTRIBUTION_ID), "516444");
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_migrate.js b/browser/components/places/tests/unit/test_browserGlue_migrate.js
new file mode 100644
index 0000000000..f0f88d2d17
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_migrate.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that nsBrowserGlue does not overwrite bookmarks imported from the
+ * migrators. They usually run before nsBrowserGlue, so if we find any
+ * bookmark on init, we should not try to import.
+ */
+
+function run_test() {
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ // Remove current database file.
+ clearDB();
+
+ run_next_test();
+}
+
+registerCleanupFunction(remove_bookmarks_html);
+
+add_task(async function test_migrate_bookmarks() {
+ // Initialize Places through the History Service and check that a new
+ // database has been created.
+ Assert.equal(
+ PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CREATE
+ );
+
+ // A migrator would run before nsBrowserGlue Places initialization, so mimic
+ // that behavior adding a bookmark and notifying the migration.
+ let bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
+ bg.observe(null, "initial-migration-will-import-default-bookmarks", null);
+
+ await PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: PlacesUtils.bookmarks.DEFAULT_INDEX,
+ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
+ url: "http://mozilla.org/",
+ title: "migrated",
+ });
+
+ let promise = promiseTopicObserved("places-browser-init-complete");
+ bg.observe(null, "initial-migration-did-import-default-bookmarks", null);
+ await promise;
+
+ // Check the created bookmark still exists.
+ let bm = await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: 0,
+ });
+ Assert.equal(bm.title, "migrated");
+
+ // Check that we have not imported any new bookmark.
+ Assert.ok(
+ !(await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: 1,
+ }))
+ );
+
+ Assert.ok(
+ !(await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ }))
+ );
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_prefs.js b/browser/components/places/tests/unit/test_browserGlue_prefs.js
new file mode 100644
index 0000000000..af1dc3db0e
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_prefs.js
@@ -0,0 +1,161 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that nsBrowserGlue is correctly interpreting the preferences settable
+ * by the user or by other components.
+ */
+
+const PREF_IMPORT_BOOKMARKS_HTML = "browser.places.importBookmarksHTML";
+const PREF_RESTORE_DEFAULT_BOOKMARKS =
+ "browser.bookmarks.restore_default_bookmarks";
+const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
+
+const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
+const TOPICDATA_FORCE_PLACES_INIT = "test-force-places-init";
+
+var bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
+
+add_task(async function setup() {
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ remove_all_JSON_backups();
+
+ // Create our JSON backup from bookmarks.glue.json.
+ create_JSON_backup("bookmarks.glue.json");
+
+ registerCleanupFunction(function () {
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+
+ return PlacesUtils.bookmarks.eraseEverything();
+ });
+});
+
+function simulatePlacesInit() {
+ info("Simulate Places init");
+ // Force nsBrowserGlue::_initPlaces().
+ bg.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT);
+ return promiseTopicObserved("places-browser-init-complete");
+}
+
+add_task(async function test_checkPreferences() {
+ // Initialize Places through the History Service and check that a new
+ // database has been created.
+ let promiseComplete = promiseTopicObserved("places-browser-init-complete");
+ Assert.equal(
+ PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CREATE
+ );
+ await promiseComplete;
+
+ // Ensure preferences status.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
+
+ Assert.throws(
+ () => Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML),
+ /NS_ERROR_UNEXPECTED/
+ );
+ Assert.throws(
+ () => Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS),
+ /NS_ERROR_UNEXPECTED/
+ );
+});
+
+add_task(async function test_import() {
+ info("Import from bookmarks.html if importBookmarksHTML is true.");
+
+ await PlacesUtils.bookmarks.eraseEverything();
+
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.ok(
+ !(await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ }))
+ );
+
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+
+ await simulatePlacesInit();
+
+ // Check bookmarks.html has been imported.
+ let bm = await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ });
+ Assert.equal(bm.title, "example");
+
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+});
+
+add_task(async function test_restore() {
+ info(
+ "restore from default bookmarks.html if " +
+ "restore_default_bookmarks is true."
+ );
+
+ await PlacesUtils.bookmarks.eraseEverything();
+
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.ok(
+ !(await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ }))
+ );
+
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
+
+ await simulatePlacesInit();
+
+ // Check bookmarks.html has been restored.
+ Assert.ok(
+ await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ })
+ );
+
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+});
+
+add_task(async function test_restore_import() {
+ info(
+ "setting both importBookmarksHTML and " +
+ "restore_default_bookmarks should restore defaults."
+ );
+
+ await PlacesUtils.bookmarks.eraseEverything();
+
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.ok(
+ !(await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ }))
+ );
+
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+ Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
+
+ await simulatePlacesInit();
+
+ // Check bookmarks.html has been restored.
+ Assert.ok(
+ await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ })
+ );
+
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_restore.js b/browser/components/places/tests/unit/test_browserGlue_restore.js
new file mode 100644
index 0000000000..98c8d1d2d6
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_restore.js
@@ -0,0 +1,55 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Tests that nsBrowserGlue correctly restores bookmarks from a JSON backup if
+ * database has been created and one backup is available.
+ */
+
+function run_test() {
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ remove_all_JSON_backups();
+
+ // Create our JSON backup from bookmarks.glue.json.
+ create_JSON_backup("bookmarks.glue.json");
+
+ // Remove current database file.
+ clearDB();
+
+ run_next_test();
+}
+
+registerCleanupFunction(function () {
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+ return PlacesUtils.bookmarks.eraseEverything();
+});
+
+add_task(async function test_main() {
+ // Initialize nsBrowserGlue before Places.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsISupports);
+
+ // Initialize Places through the History Service.
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].getService(
+ Ci.nsINavHistoryService
+ );
+
+ // Check a new database has been created.
+ // nsBrowserGlue uses databaseStatus to manage initialization.
+ Assert.equal(hs.databaseStatus, hs.DATABASE_STATUS_CREATE);
+
+ // The test will continue once restore has finished.
+ await promiseTopicObserved("places-browser-init-complete");
+
+ // Check that JSON backup has been restored.
+ let bm = await PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0,
+ });
+ Assert.equal(bm.title, "examplejson");
+});
diff --git a/browser/components/places/tests/unit/test_clearHistory_shutdown.js b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
new file mode 100644
index 0000000000..27b432e569
--- /dev/null
+++ b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
@@ -0,0 +1,183 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Tests that requesting clear history at shutdown will really clear history.
+ */
+
+const URIS = [
+ "http://a.example1.com/",
+ "http://b.example1.com/",
+ "http://b.example2.com/",
+ "http://c.example3.com/",
+];
+
+const FTP_URL = "ftp://localhost/clearHistoryOnShutdown/";
+
+const { Sanitizer } = ChromeUtils.importESModule(
+ "resource:///modules/Sanitizer.sys.mjs"
+);
+
+// Send the profile-after-change notification to the form history component to ensure
+// that it has been initialized.
+var formHistoryStartup = Cc[
+ "@mozilla.org/satchel/form-history-startup;1"
+].getService(Ci.nsIObserver);
+formHistoryStartup.observe(null, "profile-after-change", null);
+ChromeUtils.defineESModuleGetters(this, {
+ FormHistory: "resource://gre/modules/FormHistory.sys.mjs",
+});
+
+var timeInMicroseconds = Date.now() * 1000;
+
+add_task(async function test_execute() {
+ info("Initialize browserglue before Places");
+
+ // Avoid default bookmarks import.
+ let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(
+ Ci.nsIObserver
+ );
+ glue.observe(null, "initial-migration-will-import-default-bookmarks", null);
+ Sanitizer.onStartup();
+
+ Services.prefs.setBoolPref(Sanitizer.PREF_SHUTDOWN_BRANCH + "cache", true);
+ Services.prefs.setBoolPref(Sanitizer.PREF_SHUTDOWN_BRANCH + "cookies", true);
+ Services.prefs.setBoolPref(
+ Sanitizer.PREF_SHUTDOWN_BRANCH + "offlineApps",
+ true
+ );
+ Services.prefs.setBoolPref(Sanitizer.PREF_SHUTDOWN_BRANCH + "history", true);
+ Services.prefs.setBoolPref(
+ Sanitizer.PREF_SHUTDOWN_BRANCH + "downloads",
+ true
+ );
+ Services.prefs.setBoolPref(Sanitizer.PREF_SHUTDOWN_BRANCH + "cookies", true);
+ Services.prefs.setBoolPref(Sanitizer.PREF_SHUTDOWN_BRANCH + "formData", true);
+ Services.prefs.setBoolPref(Sanitizer.PREF_SHUTDOWN_BRANCH + "sessions", true);
+ Services.prefs.setBoolPref(
+ Sanitizer.PREF_SHUTDOWN_BRANCH + "siteSettings",
+ true
+ );
+
+ Services.prefs.setBoolPref(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN, true);
+
+ info("Add visits.");
+ for (let aUrl of URIS) {
+ await PlacesTestUtils.addVisits({
+ uri: uri(aUrl),
+ visitDate: timeInMicroseconds++,
+ transition: PlacesUtils.history.TRANSITION_TYPED,
+ });
+ }
+ info("Add cache.");
+ await storeCache(FTP_URL, "testData");
+ info("Add form history.");
+ await addFormHistory();
+ Assert.equal(await getFormHistoryCount(), 1, "Added form history");
+
+ info("Simulate and wait shutdown.");
+ await shutdownPlaces();
+
+ Assert.equal(await getFormHistoryCount(), 0, "Form history cleared");
+
+ let stmt = DBConn(true).createStatement(
+ "SELECT id FROM moz_places WHERE url = :page_url "
+ );
+
+ try {
+ URIS.forEach(function (aUrl) {
+ stmt.params.page_url = aUrl;
+ Assert.ok(!stmt.executeStep());
+ stmt.reset();
+ });
+ } finally {
+ stmt.finalize();
+ }
+
+ info("Check cache");
+ // Check cache.
+ await checkCache(FTP_URL);
+});
+
+function addFormHistory() {
+ let now = Date.now() * 1000;
+ return FormHistory.update({
+ op: "add",
+ fieldname: "testfield",
+ value: "test",
+ timesUsed: 1,
+ firstUsed: now,
+ lastUsed: now,
+ });
+}
+
+async function getFormHistoryCount() {
+ return FormHistory.count({ fieldname: "testfield" });
+}
+
+function storeCache(aURL, aContent) {
+ let cache = Services.cache2;
+ let storage = cache.diskCacheStorage(Services.loadContextInfo.default);
+
+ return new Promise(resolve => {
+ let storeCacheListener = {
+ onCacheEntryCheck(entry) {
+ return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
+ },
+
+ onCacheEntryAvailable(entry, isnew, status) {
+ Assert.equal(status, Cr.NS_OK);
+
+ entry.setMetaDataElement("servertype", "0");
+ var os = entry.openOutputStream(0, -1);
+
+ var written = os.write(aContent, aContent.length);
+ if (written != aContent.length) {
+ do_throw(
+ "os.write has not written all data!\n" +
+ " Expected: " +
+ written +
+ "\n" +
+ " Actual: " +
+ aContent.length +
+ "\n"
+ );
+ }
+ os.close();
+ entry.close();
+ resolve();
+ },
+ };
+
+ storage.asyncOpenURI(
+ Services.io.newURI(aURL),
+ "",
+ Ci.nsICacheStorage.OPEN_NORMALLY,
+ storeCacheListener
+ );
+ });
+}
+
+function checkCache(aURL) {
+ let cache = Services.cache2;
+ let storage = cache.diskCacheStorage(Services.loadContextInfo.default);
+
+ return new Promise(resolve => {
+ let checkCacheListener = {
+ onCacheEntryAvailable(entry, isnew, status) {
+ Assert.equal(status, Cr.NS_ERROR_CACHE_KEY_NOT_FOUND);
+ resolve();
+ },
+ };
+
+ storage.asyncOpenURI(
+ Services.io.newURI(aURL),
+ "",
+ Ci.nsICacheStorage.OPEN_READONLY,
+ checkCacheListener
+ );
+ });
+}
diff --git a/browser/components/places/tests/unit/test_interactions_blocklist.js b/browser/components/places/tests/unit/test_interactions_blocklist.js
new file mode 100644
index 0000000000..0e81f80af2
--- /dev/null
+++ b/browser/components/places/tests/unit/test_interactions_blocklist.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that blocked sites are caught by InteractionsBlocklist.
+ */
+
+ChromeUtils.defineESModuleGetters(this, {
+ InteractionsBlocklist: "resource:///modules/InteractionsBlocklist.sys.mjs",
+});
+
+let BLOCKED_URLS = [
+ "https://www.bing.com/search?q=mozilla",
+ "https://duckduckgo.com/?q=a+test&kp=1&t=ffab",
+ "https://www.google.com/search?q=mozilla",
+ "https://www.google.ca/search?q=test",
+ "https://mozilla.zoom.us/j/123456789",
+ "https://yandex.az/search/?text=mozilla",
+ "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&ch=&tn=baidu&bar=&wd=mozilla&rn=&fenlei=256&oq=&rsv_pq=970f2b8f001757b9&rsv_t=1f5d2V2o80HPdZtZnhodwkc7nZXTvDI1zwdPy%2FAeomnvFFGIrU1F3D9WoK4&rqlang=cn",
+ "https://accounts.google.com/o/oauth2/v2/auth/identifier/foobar",
+ "https://auth.mozilla.auth0.com/login/foobar",
+ "https://accounts.google.com/signin/oauth/consent/foobar",
+ "https://accounts.google.com/o/oauth2/v2/auth?client_id=ZZZ",
+ "https://login.microsoftonline.com/common/oauth2/v2.0/authorize/foobar",
+];
+
+let ALLOWED_URLS = [
+ "https://example.com",
+ "https://zoom.us/pricing",
+ "https://www.google.ca/maps/place/Toronto,+ON/@43.7181557,-79.5181414,11z/data=!3m1!4b1!4m5!3m4!1s0x89d4cb90d7c63ba5:0x323555502ab4c477!8m2!3d43.653226!4d-79.3831843",
+ "https://example.com/https://auth.mozilla.auth0.com/login/foobar",
+];
+
+// Tests that initializing InteractionsBlocklist loads the regexes from the
+// customBlocklist pref on initialization. This subtest should always be the
+// first one in this file.
+add_task(async function blockedOnInit() {
+ Services.prefs.setStringPref(
+ "places.interactions.customBlocklist",
+ '["^(https?:\\\\/\\\\/)?mochi.test"]'
+ );
+ Assert.ok(
+ InteractionsBlocklist.isUrlBlocklisted("https://mochi.test"),
+ "mochi.test is blocklisted."
+ );
+ InteractionsBlocklist.removeRegexFromBlocklist("^(https?:\\/\\/)?mochi.test");
+ Assert.ok(
+ !InteractionsBlocklist.isUrlBlocklisted("https://mochi.test"),
+ "mochi.test is not blocklisted."
+ );
+});
+
+add_task(async function test() {
+ for (let url of BLOCKED_URLS) {
+ Assert.ok(
+ InteractionsBlocklist.isUrlBlocklisted(url),
+ `${url} is blocklisted.`
+ );
+ }
+
+ for (let url of ALLOWED_URLS) {
+ Assert.ok(
+ !InteractionsBlocklist.isUrlBlocklisted(url),
+ `${url} is not blocklisted.`
+ );
+ }
+});
diff --git a/browser/components/places/tests/unit/test_invalid_defaultLocation.js b/browser/components/places/tests/unit/test_invalid_defaultLocation.js
new file mode 100644
index 0000000000..e533d051d0
--- /dev/null
+++ b/browser/components/places/tests/unit/test_invalid_defaultLocation.js
@@ -0,0 +1,24 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+/**
+ * Test that if browser.bookmarks.defaultLocation contains an invalid GUID,
+ * PlacesUIUtils.defaultParentGuid will return a proper default value.
+ */
+
+add_task(async function () {
+ Services.prefs.setCharPref(
+ "browser.bookmarks.defaultLocation",
+ "useOtherBookmarks"
+ );
+
+ info(
+ "Checking that default parent guid was set back to the toolbar because of invalid preferable guid"
+ );
+ Assert.equal(
+ await PlacesUIUtils.defaultParentGuid,
+ PlacesUtils.bookmarks.toolbarGuid,
+ "Default parent guid is a toolbar guid"
+ );
+});
diff --git a/browser/components/places/tests/unit/xpcshell.toml b/browser/components/places/tests/unit/xpcshell.toml
new file mode 100644
index 0000000000..75727e359a
--- /dev/null
+++ b/browser/components/places/tests/unit/xpcshell.toml
@@ -0,0 +1,38 @@
+[DEFAULT]
+head = "head_bookmarks.js"
+firefox-appdir = "browser"
+skip-if = ["os == 'android'"] # bug 1730213
+support-files = [
+ "bookmarks.glue.html",
+ "bookmarks.glue.json",
+ "corruptDB.sqlite",
+ "distribution.ini",
+]
+
+["test_PUIU_batchUpdatesForNode.js"]
+
+["test_PUIU_setCharsetForPage.js"]
+
+["test_PUIU_title_difference_spotter.js"]
+
+["test_browserGlue_bookmarkshtml.js"]
+
+["test_browserGlue_corrupt.js"]
+
+["test_browserGlue_corrupt_nobackup.js"]
+
+["test_browserGlue_corrupt_nobackup_default.js"]
+
+["test_browserGlue_distribution.js"]
+
+["test_browserGlue_migrate.js"]
+
+["test_browserGlue_prefs.js"]
+
+["test_browserGlue_restore.js"]
+
+["test_clearHistory_shutdown.js"]
+
+["test_interactions_blocklist.js"]
+
+["test_invalid_defaultLocation.js"]