Bug 1962056 - move places initialization out of BrowserGlue, r=places-reviewers,migration-reviewers,firefox-desktop-core-reviewers ,mossop,mak,mconley

Places frontend initialization is surprisingly complex, and disentangling it
from the rest of startup by moving it to its own file helps make some of the
logic a little more obvious, and makes unit-testing a bit easier.

This also removes BrowserGlue from the indirection mechanism used between
MigatorBase instances and Places, by switching to category-manager-based
invocation - this way, migrator code does not need to directly
call places code to tell it it's done, but we don't need BrowserGlue to play
messenger between them.

It would be nice to do the same thing for `places-init-complete` but
that is notified from C++ code so unfortunately that is not easily possible.

Differential Revision: https://phabricator.services.mozilla.com/D244428
This commit is contained in:
Gijs Kruitbosch
2025-05-21 13:13:20 +00:00
committed by gijskruitbosch@gmail.com
parent b1d5730698
commit 78ebe33ed8
13 changed files with 519 additions and 474 deletions

View File

@@ -16,10 +16,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
"resource:///modules/asrouter/ASRouterDefaultConfig.sys.mjs", "resource:///modules/asrouter/ASRouterDefaultConfig.sys.mjs",
ASRouterNewTabHook: "resource:///modules/asrouter/ASRouterNewTabHook.sys.mjs", ASRouterNewTabHook: "resource:///modules/asrouter/ASRouterNewTabHook.sys.mjs",
AddonManager: "resource://gre/modules/AddonManager.sys.mjs", AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
BackupService: "resource:///modules/backup/BackupService.sys.mjs", BackupService: "resource:///modules/backup/BackupService.sys.mjs",
BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.sys.mjs",
BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.sys.mjs",
BrowserSearchTelemetry: BrowserSearchTelemetry:
"moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs", "moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs",
BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
@@ -38,6 +35,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
DesktopActorRegistry: DesktopActorRegistry:
"moz-src:///browser/components/DesktopActorRegistry.sys.mjs", "moz-src:///browser/components/DesktopActorRegistry.sys.mjs",
Discovery: "resource:///modules/Discovery.sys.mjs", Discovery: "resource:///modules/Discovery.sys.mjs",
DistributionManagement: "resource:///modules/distribution.sys.mjs",
DoHController: "resource://gre/modules/DoHController.sys.mjs", DoHController: "resource://gre/modules/DoHController.sys.mjs",
DownloadsViewableInternally: DownloadsViewableInternally:
"resource:///modules/DownloadsViewableInternally.sys.mjs", "resource:///modules/DownloadsViewableInternally.sys.mjs",
@@ -58,9 +56,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
PageDataService: "resource:///modules/pagedata/PageDataService.sys.mjs", PageDataService: "resource:///modules/pagedata/PageDataService.sys.mjs",
PageThumbs: "resource://gre/modules/PageThumbs.sys.mjs", PageThumbs: "resource://gre/modules/PageThumbs.sys.mjs",
PdfJs: "resource://pdf.js/PdfJs.sys.mjs", PdfJs: "resource://pdf.js/PdfJs.sys.mjs",
PlacesBackups: "resource://gre/modules/PlacesBackups.sys.mjs", PlacesBrowserStartup:
PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs", "moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs",
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
// PluginManager is used by the `listeners` object below. // PluginManager is used by the `listeners` object below.
// eslint-disable-next-line mozilla/valid-lazy // eslint-disable-next-line mozilla/valid-lazy
PluginManager: "resource:///actors/PluginParent.sys.mjs", PluginManager: "resource:///actors/PluginParent.sys.mjs",
@@ -187,11 +184,6 @@ if (AppConstants.MOZ_UPDATER) {
listeners.observers["update-swap"] = ["UpdateListener"]; listeners.observers["update-swap"] = ["UpdateListener"];
} }
// Seconds of idle before trying to create a bookmarks backup.
const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
// Minimum interval between backups. We try to not create more than one backup
// per interval.
const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
// Seconds of idle time before the late idle tasks will be scheduled. // Seconds of idle time before the late idle tasks will be scheduled.
const LATE_TASKS_IDLE_TIME_SEC = 20; const LATE_TASKS_IDLE_TIME_SEC = 20;
// Time after we stop tracking startup crashes. // Time after we stop tracking startup crashes.
@@ -222,20 +214,11 @@ export function BrowserGlue() {
"nsIUserIdleService" "nsIUserIdleService"
); );
ChromeUtils.defineLazyGetter(this, "_distributionCustomizer", function () {
const { DistributionCustomizer } = ChromeUtils.importESModule(
"resource:///modules/distribution.sys.mjs"
);
return new DistributionCustomizer();
});
this._init(); this._init();
} }
BrowserGlue.prototype = { BrowserGlue.prototype = {
_saveSession: false, _saveSession: false,
_migrationImportsDefaultBookmarks: false,
_placesBrowserInitComplete: false,
_isNewProfile: undefined, _isNewProfile: undefined,
_defaultCookieBehaviorAtStartup: null, _defaultCookieBehaviorAtStartup: null,
@@ -306,44 +289,17 @@ BrowserGlue.prototype = {
break; break;
case "places-init-complete": case "places-init-complete":
Services.obs.removeObserver(this, "places-init-complete"); Services.obs.removeObserver(this, "places-init-complete");
if (!this._migrationImportsDefaultBookmarks) { lazy.PlacesBrowserStartup.backendInitComplete();
this._initPlaces(false);
}
break;
case "idle":
this._backupBookmarks();
break;
case "distribution-customization-complete":
Services.obs.removeObserver(
this,
"distribution-customization-complete"
);
// Customization has finished, we don't need the customizer anymore.
delete this._distributionCustomizer;
break; break;
case "browser-glue-test": // used by tests case "browser-glue-test": // used by tests
if (data == "force-ui-migration") { if (data == "force-ui-migration") {
this._migrateUI(); this._migrateUI();
} else if (data == "force-distribution-customization") {
this._distributionCustomizer.applyCustomizations();
// To apply distribution bookmarks use "places-init-complete".
} else if (data == "test-force-places-init") {
this._placesInitialized = false;
this._initPlaces(false);
} else if (data == "places-browser-init-complete") { } else if (data == "places-browser-init-complete") {
if (this._placesBrowserInitComplete) { lazy.PlacesBrowserStartup.notifyIfInitializationComplete();
Services.obs.notifyObservers(null, "places-browser-init-complete");
}
} else if (data == "add-breaches-sync-handler") { } else if (data == "add-breaches-sync-handler") {
this._addBreachesSyncHandler(); this._addBreachesSyncHandler();
} }
break; break;
case "initial-migration-will-import-default-bookmarks":
this._migrationImportsDefaultBookmarks = true;
break;
case "initial-migration-did-import-default-bookmarks":
this._initPlaces(true);
break;
case "handle-xul-text-link": { case "handle-xul-text-link": {
let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool); let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool);
if (!linkHandled.data) { if (!linkHandled.data) {
@@ -363,8 +319,7 @@ BrowserGlue.prototype = {
break; break;
} }
case "profile-before-change": case "profile-before-change":
// Any component depending on Places should be finalized in // Any component that doesn't need to act after
// _onPlacesShutdown. Any component that doesn't need to act after
// the UI has gone should be finalized in _onQuitApplicationGranted. // the UI has gone should be finalized in _onQuitApplicationGranted.
this._dispose(); this._dispose();
break; break;
@@ -440,7 +395,6 @@ BrowserGlue.prototype = {
"quit-application-granted", "quit-application-granted",
"session-save", "session-save",
"places-init-complete", "places-init-complete",
"distribution-customization-complete",
"handle-xul-text-link", "handle-xul-text-link",
"profile-before-change", "profile-before-change",
"keyword-search", "keyword-search",
@@ -454,10 +408,6 @@ BrowserGlue.prototype = {
} }
lazy.DesktopActorRegistry.init(); lazy.DesktopActorRegistry.init();
this._firstWindowReady = new Promise(
resolve => (this._firstWindowLoaded = resolve)
);
}, },
// cleanup (called on application shutdown) // cleanup (called on application shutdown)
@@ -467,13 +417,6 @@ BrowserGlue.prototype = {
// until here. // until here.
lazy.AboutHomeStartupCache.uninit(); lazy.AboutHomeStartupCache.uninit();
if (this._bookmarksBackupIdleTime) {
this._userIdleService.removeIdleObserver(
this,
this._bookmarksBackupIdleTime
);
this._bookmarksBackupIdleTime = null;
}
if (this._lateTasksIdleObserver) { if (this._lateTasksIdleObserver) {
this._userIdleService.removeIdleObserver( this._userIdleService.removeIdleObserver(
this._lateTasksIdleObserver, this._lateTasksIdleObserver,
@@ -506,7 +449,7 @@ BrowserGlue.prototype = {
} }
// apply distribution customizations // apply distribution customizations
this._distributionCustomizer.applyCustomizations(); lazy.DistributionManagement.applyCustomizations();
// handle any UI migration // handle any UI migration
this._migrateUI(); this._migrateUI();
@@ -923,17 +866,19 @@ BrowserGlue.prototype = {
lazy.SelectableProfileService.init().catch(console.error); lazy.SelectableProfileService.init().catch(console.error);
this._firstWindowTelemetry(aWindow); this._firstWindowTelemetry(aWindow);
this._firstWindowLoaded();
// Set the default favicon size for UI views that use the page-icon protocol.
lazy.PlacesUtils.favicons.setDefaultIconURIPreferredSize(
16 * aWindow.devicePixelRatio
);
lazy.ContentBlockingPrefs.init(); lazy.ContentBlockingPrefs.init();
lazy.CaptchaDetectionPingUtils.init(); lazy.CaptchaDetectionPingUtils.init();
this._verifySandboxUserNamespaces(aWindow); this._verifySandboxUserNamespaces(aWindow);
lazy.BrowserUtils.callModulesFromCategory(
{
categoryName: "browser-first-window-ready",
profilerMarker: "browserFirstWindowReady",
},
aWindow
);
}, },
_maybeOfferProfileReset() { _maybeOfferProfileReset() {
@@ -1019,16 +964,6 @@ BrowserGlue.prototype = {
// yet occurred (see trackStartupCrashEnd caller in browser.js). // yet occurred (see trackStartupCrashEnd caller in browser.js).
() => Services.startup.trackStartupCrashEnd(), () => Services.startup.trackStartupCrashEnd(),
() => {
if (this._bookmarksBackupIdleTime) {
this._userIdleService.removeIdleObserver(
this,
this._bookmarksBackupIdleTime
);
this._bookmarksBackupIdleTime = null;
}
},
() => { () => {
// bug 1839426 - The FOG service needs to be instantiated reliably so it // bug 1839426 - The FOG service needs to be instantiated reliably so it
// can perform at-shutdown tasks later in shutdown. // can perform at-shutdown tasks later in shutdown.
@@ -1333,37 +1268,6 @@ BrowserGlue.prototype = {
}, },
}, },
// Add the import button if this is the first startup.
{
name: "PlacesUIUtils.ImportButton",
task: async () => {
// First check if we've already added the import button, in which
// case we should check for events indicating we can remove it.
if (
Services.prefs.getBoolPref(
"browser.bookmarks.addedImportButton",
false
)
) {
lazy.PlacesUIUtils.removeImportButtonWhenImportSucceeds();
return;
}
// Otherwise, check if this is a new profile where we need to add it.
// `maybeAddImportButton` will call
// `removeImportButtonWhenImportSucceeds`itself if/when it adds the
// button. Doing things in this order avoids listening for removal
// more than once.
if (
this._isNewProfile &&
// Not in automation: the button changes CUI state, breaking tests
!Cu.isInAutomation
) {
await lazy.PlacesUIUtils.maybeAddImportButton();
}
},
},
// Add the setup button if this is the first startup // Add the setup button if this is the first startup
{ {
name: "AWToolbarButton.SetupButton", name: "AWToolbarButton.SetupButton",
@@ -1785,269 +1689,6 @@ BrowserGlue.prototype = {
aCancelQuit.data = buttonPressed != 0; aCancelQuit.data = buttonPressed != 0;
}, },
/**
* Initialize Places
* - imports the bookmarks html file if bookmarks database is empty, try to
* restore bookmarks from a JSON backup if the backend indicates that the
* database was corrupt.
*
* These prefs can be set up by the frontend:
*
* WARNING: setting these preferences to true will overwite existing bookmarks
*
* - browser.places.importBookmarksHTML
* Set to true will import the bookmarks.html file from the profile folder.
* - browser.bookmarks.restore_default_bookmarks
* Set to true by safe-mode dialog to indicate we must restore default
* bookmarks.
*/
_initPlaces: function BG__initPlaces(aInitialMigrationPerformed) {
if (this._placesInitialized) {
throw new Error("Cannot initialize Places more than once");
}
this._placesInitialized = true;
// We must instantiate the history service since it will tell us if we
// need to import or restore bookmarks due to first-run, corruption or
// forced migration (due to a major schema change).
// If the database is corrupt or has been newly created we should
// import bookmarks.
let dbStatus = lazy.PlacesUtils.history.databaseStatus;
// Show a notification with a "more info" link for a locked places.sqlite.
if (dbStatus == lazy.PlacesUtils.history.DATABASE_STATUS_LOCKED) {
// Note: initPlaces should always happen when the first window is ready,
// in any case, better safe than sorry.
this._firstWindowReady.then(() => {
this._showPlacesLockedNotificationBox();
this._placesBrowserInitComplete = true;
Services.obs.notifyObservers(null, "places-browser-init-complete");
});
return;
}
let importBookmarks =
!aInitialMigrationPerformed &&
(dbStatus == lazy.PlacesUtils.history.DATABASE_STATUS_CREATE ||
dbStatus == lazy.PlacesUtils.history.DATABASE_STATUS_CORRUPT);
// Check if user or an extension has required to import bookmarks.html
let importBookmarksHTML = false;
try {
importBookmarksHTML = Services.prefs.getBoolPref(
"browser.places.importBookmarksHTML"
);
if (importBookmarksHTML) {
importBookmarks = true;
}
} catch (ex) {}
// Support legacy bookmarks.html format for apps that depend on that format.
let autoExportHTML = Services.prefs.getBoolPref(
"browser.bookmarks.autoExportHTML",
false
); // Do not export.
if (autoExportHTML) {
// Sqlite.sys.mjs and Places shutdown happen at profile-before-change, thus,
// to be on the safe side, this should run earlier.
lazy.AsyncShutdown.profileChangeTeardown.addBlocker(
"Places: export bookmarks.html",
() =>
lazy.BookmarkHTMLUtils.exportToFile(
lazy.BookmarkHTMLUtils.defaultPath
)
);
}
(async () => {
// Check if Safe Mode or the user has required to restore bookmarks from
// default profile's bookmarks.html
let restoreDefaultBookmarks = false;
try {
restoreDefaultBookmarks = Services.prefs.getBoolPref(
"browser.bookmarks.restore_default_bookmarks"
);
if (restoreDefaultBookmarks) {
// Ensure that we already have a bookmarks backup for today.
await this._backupBookmarks();
importBookmarks = true;
}
} catch (ex) {}
// If the user did not require to restore default bookmarks, or import
// from bookmarks.html, we will try to restore from JSON
if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) {
// get latest JSON backup
let lastBackupFile = await lazy.PlacesBackups.getMostRecentBackup();
if (lastBackupFile) {
// restore from JSON backup
await lazy.BookmarkJSONUtils.importFromFile(lastBackupFile, {
replace: true,
source: lazy.PlacesUtils.bookmarks.SOURCES.RESTORE_ON_STARTUP,
});
importBookmarks = false;
} else {
// We have created a new database but we don't have any backup available
importBookmarks = true;
if (await IOUtils.exists(lazy.BookmarkHTMLUtils.defaultPath)) {
// If bookmarks.html is available in current profile import it...
importBookmarksHTML = true;
} else {
// ...otherwise we will restore defaults
restoreDefaultBookmarks = true;
}
}
}
// Import default bookmarks when necessary.
// Otherwise, if any kind of import runs, default bookmarks creation should be
// delayed till the import operations has finished. Not doing so would
// cause them to be overwritten by the newly imported bookmarks.
if (!importBookmarks) {
// Now apply distribution customized bookmarks.
// This should always run after Places initialization.
try {
await this._distributionCustomizer.applyBookmarks();
} catch (e) {
console.error(e);
}
} else {
// An import operation is about to run.
let bookmarksUrl = null;
if (restoreDefaultBookmarks) {
// User wants to restore the default set of bookmarks shipped with the
// browser, those that new profiles start with.
bookmarksUrl = "chrome://browser/content/default-bookmarks.html";
} else if (await IOUtils.exists(lazy.BookmarkHTMLUtils.defaultPath)) {
bookmarksUrl = PathUtils.toFileURI(
lazy.BookmarkHTMLUtils.defaultPath
);
}
if (bookmarksUrl) {
// Import from bookmarks.html file.
try {
if (
Services.policies.isAllowed("defaultBookmarks") &&
// Default bookmarks are imported after startup, and they may
// influence the outcome of tests, thus it's possible to use
// this test-only pref to skip the import.
!(
Cu.isInAutomation &&
Services.prefs.getBoolPref(
"browser.bookmarks.testing.skipDefaultBookmarksImport",
false
)
)
) {
await lazy.BookmarkHTMLUtils.importFromURL(bookmarksUrl, {
replace: true,
source: lazy.PlacesUtils.bookmarks.SOURCES.RESTORE_ON_STARTUP,
});
}
} catch (e) {
console.error("Bookmarks.html file could be corrupt. ", e);
}
try {
// Now apply distribution customized bookmarks.
// This should always run after Places initialization.
await this._distributionCustomizer.applyBookmarks();
} catch (e) {
console.error(e);
}
} else {
console.error(new Error("Unable to find bookmarks.html file."));
}
// Reset preferences, so we won't try to import again at next run
if (importBookmarksHTML) {
Services.prefs.setBoolPref(
"browser.places.importBookmarksHTML",
false
);
}
if (restoreDefaultBookmarks) {
Services.prefs.setBoolPref(
"browser.bookmarks.restore_default_bookmarks",
false
);
}
}
// Initialize bookmark archiving on idle.
// If the last backup has been created before the last browser session,
// and is days old, be more aggressive with the idle timer.
let idleTime = BOOKMARKS_BACKUP_IDLE_TIME_SEC;
if (!(await lazy.PlacesBackups.hasRecentBackup())) {
idleTime /= 2;
}
if (!this._isObservingIdle) {
this._userIdleService.addIdleObserver(this, idleTime);
this._isObservingIdle = true;
}
this._bookmarksBackupIdleTime = idleTime;
if (this._isNewProfile) {
// New profiles may have existing bookmarks (imported from another browser or
// copied into the profile) and we want to show the bookmark toolbar for them
// in some cases.
await lazy.PlacesUIUtils.maybeToggleBookmarkToolbarVisibility();
}
})()
.catch(ex => {
console.error(ex);
})
.then(() => {
// NB: deliberately after the catch so that we always do this, even if
// we threw halfway through initializing in the Task above.
this._placesBrowserInitComplete = true;
Services.obs.notifyObservers(null, "places-browser-init-complete");
});
},
/**
* If a backup for today doesn't exist, this creates one.
*/
_backupBookmarks: function BG__backupBookmarks() {
return (async function () {
let lastBackupFile = await lazy.PlacesBackups.getMostRecentBackup();
// Should backup bookmarks if there are no backups or the maximum
// interval between backups elapsed.
if (
!lastBackupFile ||
new Date() - lazy.PlacesBackups.getDateForFile(lastBackupFile) >
BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS * 86400000
) {
let maxBackups = Services.prefs.getIntPref(
"browser.bookmarks.max_backups"
);
await lazy.PlacesBackups.create(maxBackups);
}
})();
},
/**
* Show the notificationBox for a locked places database.
*/
_showPlacesLockedNotificationBox:
async function BG__showPlacesLockedNotificationBox() {
var win = lazy.BrowserWindowTracker.getTopWindow();
var buttons = [{ supportPage: "places-locked" }];
var notifyBox = win.gBrowser.getNotificationBox();
var notification = await notifyBox.appendNotification(
"places-locked",
{
label: { "l10n-id": "places-locked-prompt" },
priority: win.gNotificationBox.PRIORITY_CRITICAL_MEDIUM,
},
buttons
);
notification.persistence = -1; // Until user closes it
},
_migrateUI() { _migrateUI() {
// Use an increasing number to keep track of the current state of the user's // Use an increasing number to keep track of the current state of the user's
// profile, so we can move data around as needed as the browser evolves. // profile, so we can move data around as needed as the browser evolves.

View File

@@ -8,6 +8,11 @@ const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC =
const PREF_CACHED_FILE_EXISTENCE = "distribution.iniFile.exists.value"; const PREF_CACHED_FILE_EXISTENCE = "distribution.iniFile.exists.value";
const PREF_CACHED_FILE_APPVERSION = "distribution.iniFile.exists.appversion"; const PREF_CACHED_FILE_APPVERSION = "distribution.iniFile.exists.appversion";
// These prefixes must only contain characters
// allowed by PlacesUtils.isValidGuid
const BOOKMARK_GUID_PREFIX = "DstB-";
const FOLDER_GUID_PREFIX = "DstF-";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
const lazy = {}; const lazy = {};
@@ -19,11 +24,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
export function DistributionCustomizer() {} export function DistributionCustomizer() {}
DistributionCustomizer.prototype = { DistributionCustomizer.prototype = {
// These prefixes must only contain characters
// allowed by PlacesUtils.isValidGuid
BOOKMARK_GUID_PREFIX: "DstB-",
FOLDER_GUID_PREFIX: "DstF-",
get _iniFile() { get _iniFile() {
// For parallel xpcshell testing purposes allow loading the distribution.ini // For parallel xpcshell testing purposes allow loading the distribution.ini
// file from the profile folder through an hidden pref. // file from the profile folder through an hidden pref.
@@ -114,12 +114,12 @@ DistributionCustomizer.prototype = {
async _removeDistributionBookmarks() { async _removeDistributionBookmarks() {
await lazy.PlacesUtils.bookmarks.fetch( await lazy.PlacesUtils.bookmarks.fetch(
{ guidPrefix: this.BOOKMARK_GUID_PREFIX }, { guidPrefix: BOOKMARK_GUID_PREFIX },
bookmark => bookmark =>
lazy.PlacesUtils.bookmarks.remove(bookmark).catch(console.error) lazy.PlacesUtils.bookmarks.remove(bookmark).catch(console.error)
); );
await lazy.PlacesUtils.bookmarks.fetch( await lazy.PlacesUtils.bookmarks.fetch(
{ guidPrefix: this.FOLDER_GUID_PREFIX }, { guidPrefix: FOLDER_GUID_PREFIX },
folder => { folder => {
lazy.PlacesUtils.bookmarks.remove(folder).catch(console.error); lazy.PlacesUtils.bookmarks.remove(folder).catch(console.error);
} }
@@ -179,16 +179,14 @@ DistributionCustomizer.prototype = {
case "default": case "default":
break; break;
case "folder": case "folder": {
if (itemIndex < defaultIndex) { if (itemIndex < defaultIndex) {
index = prependIndex++; index = prependIndex++;
} }
let folder = await lazy.PlacesUtils.bookmarks.insert({ let folder = await lazy.PlacesUtils.bookmarks.insert({
type: lazy.PlacesUtils.bookmarks.TYPE_FOLDER, type: lazy.PlacesUtils.bookmarks.TYPE_FOLDER,
guid: lazy.PlacesUtils.generateGuidWithPrefix( guid: lazy.PlacesUtils.generateGuidWithPrefix(FOLDER_GUID_PREFIX),
this.FOLDER_GUID_PREFIX
),
parentGuid, parentGuid,
index, index,
title: item.title, title: item.title,
@@ -199,6 +197,7 @@ DistributionCustomizer.prototype = {
"BookmarksFolder-" + item.folderId "BookmarksFolder-" + item.folderId
); );
break; break;
}
case "separator": case "separator":
if (itemIndex < defaultIndex) { if (itemIndex < defaultIndex) {
@@ -237,9 +236,7 @@ DistributionCustomizer.prototype = {
} }
await lazy.PlacesUtils.bookmarks.insert({ await lazy.PlacesUtils.bookmarks.insert({
guid: lazy.PlacesUtils.generateGuidWithPrefix( guid: lazy.PlacesUtils.generateGuidWithPrefix(BOOKMARK_GUID_PREFIX),
this.BOOKMARK_GUID_PREFIX
),
parentGuid, parentGuid,
index, index,
title: item.title, title: item.title,
@@ -654,3 +651,43 @@ function enumToObject(UTF8Enumerator) {
} }
return ret; return ret;
} }
export let DistributionManagement = {
_distributionCustomizer: null,
get BOOKMARK_GUID_PREFIX() {
return BOOKMARK_GUID_PREFIX;
},
get FOLDER_GUID_PREFIX() {
return FOLDER_GUID_PREFIX;
},
_ensureCustomizer() {
if (this._distributionCustomizer) {
return;
}
this._distributionCustomizer = new DistributionCustomizer();
Services.obs.addObserver(this, DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC);
},
observe(_subject, topic) {
if (topic == DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC) {
Services.obs.removeObserver(
this,
DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC
);
this._distributionCustomizer = null;
}
},
applyCustomizations() {
this._ensureCustomizer();
this._distributionCustomizer.applyCustomizations();
},
applyBookmarks() {
this._ensureCustomizer();
this._distributionCustomizer.applyBookmarks();
},
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
};

View File

@@ -12,6 +12,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, { ChromeUtils.defineESModuleGetters(lazy, {
BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.sys.mjs", BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.sys.mjs",
BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
FirefoxProfileMigrator: "resource:///modules/FirefoxProfileMigrator.sys.mjs", FirefoxProfileMigrator: "resource:///modules/FirefoxProfileMigrator.sys.mjs",
MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs", MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs",
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
@@ -433,11 +434,10 @@ export class MigratorBase {
// (=startupOnlyMigrator), as it just copies over the places database // (=startupOnlyMigrator), as it just copies over the places database
// from another profile. // from another profile.
await (async function () { await (async function () {
// Tell nsBrowserGlue we're importing default bookmarks. // Tell whoever cares we're importing default bookmarks.
let browserGlue = Cc["@mozilla.org/browser/browserglue;1"].getService( lazy.BrowserUtils.callModulesFromCategory({
Ci.nsIObserver categoryName: TOPIC_WILL_IMPORT_BOOKMARKS,
); });
browserGlue.observe(null, TOPIC_WILL_IMPORT_BOOKMARKS, "");
// Import the default bookmarks. We ignore whether or not we succeed. // Import the default bookmarks. We ignore whether or not we succeed.
await lazy.BookmarkHTMLUtils.importFromURL( await lazy.BookmarkHTMLUtils.importFromURL(
@@ -448,23 +448,16 @@ export class MigratorBase {
} }
).catch(console.error); ).catch(console.error);
// We'll tell nsBrowserGlue we've imported bookmarks, but before that // We'll tell places we've imported bookmarks, but before that
// we need to make sure we're going to know when it's finished // we need to make sure we're going to know when it's finished
// initializing places: // initializing:
let placesInitedPromise = new Promise(resolve => { let placesInitedPromise = lazy.BrowserUtils.promiseObserved(
let onPlacesInited = function () {
Services.obs.removeObserver(
onPlacesInited,
TOPIC_PLACES_DEFAULTS_FINISHED
);
resolve();
};
Services.obs.addObserver(
onPlacesInited,
TOPIC_PLACES_DEFAULTS_FINISHED TOPIC_PLACES_DEFAULTS_FINISHED
); );
lazy.BrowserUtils.callModulesFromCategory({
categoryName: TOPIC_DID_IMPORT_BOOKMARKS,
}); });
browserGlue.observe(null, TOPIC_DID_IMPORT_BOOKMARKS, "");
await placesInitedPromise; await placesInitedPromise;
await doMigrate(); await doMigrate();
})(); })();

View File

@@ -0,0 +1,381 @@
/* 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.sys.mjs",
BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.sys.mjs",
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
DistributionManagement: "resource:///modules/distribution.sys.mjs",
PlacesBackups: "resource://gre/modules/PlacesBackups.sys.mjs",
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs",
});
XPCOMUtils.defineLazyServiceGetters(lazy, {
BrowserHandler: ["@mozilla.org/browser/clh;1", "nsIBrowserHandler"],
UserIdleService: [
"@mozilla.org/widget/useridleservice;1",
"nsIUserIdleService",
],
});
// Seconds of idle before trying to create a bookmarks backup.
const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
// Minimum interval between backups. We try to not create more than one backup
// per interval.
const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
export let PlacesBrowserStartup = {
_migrationImportsDefaultBookmarks: false,
_placesInitialized: false,
_placesBrowserInitComplete: false,
_isObservingIdle: false,
_bookmarksBackupIdleTime: null,
_firstWindowReady: Promise.withResolvers(),
onFirstWindowReady(window) {
this._firstWindowReady.resolve();
// Set the default favicon size for UI views that use the page-icon protocol.
lazy.PlacesUtils.favicons.setDefaultIconURIPreferredSize(
16 * window.devicePixelRatio
);
},
backendInitComplete() {
if (!this._migrationImportsDefaultBookmarks) {
this.initPlaces();
}
},
willImportDefaultBookmarks() {
this._migrationImportsDefaultBookmarks = true;
},
didImportDefaultBookmarks() {
this.initPlaces({ initialMigrationPerformed: true });
},
/**
* Initialize Places
* - imports the bookmarks html file if bookmarks database is empty, try to
* restore bookmarks from a JSON backup if the backend indicates that the
* database was corrupt.
*
* These prefs can be set up by the frontend:
*
* WARNING: setting these preferences to true will overwite existing bookmarks
*
* - browser.places.importBookmarksHTML
* Set to true will import the bookmarks.html file from the profile folder.
* - browser.bookmarks.restore_default_bookmarks
* Set to true by safe-mode dialog to indicate we must restore default
* bookmarks.
*
* @param {object} [options={}]
* @param {boolean} [options.initialMigrationPerformed=false]
* Whether we performed an initial migration from another browser or via
* Firefox Refresh.
*/
initPlaces({ initialMigrationPerformed = false } = {}) {
if (this._placesInitialized) {
throw new Error("Cannot initialize Places more than once");
}
this._placesInitialized = true;
// We must instantiate the history service since it will tell us if we
// need to import or restore bookmarks due to first-run, corruption or
// forced migration (due to a major schema change).
// If the database is corrupt or has been newly created we should
// import bookmarks.
let dbStatus = lazy.PlacesUtils.history.databaseStatus;
// Show a notification with a "more info" link for a locked places.sqlite.
if (dbStatus == lazy.PlacesUtils.history.DATABASE_STATUS_LOCKED) {
// Note: initPlaces should always happen when the first window is ready,
// in any case, better safe than sorry.
this._firstWindowReady.promise.then(() => {
this._showPlacesLockedNotificationBox();
this._placesBrowserInitComplete = true;
Services.obs.notifyObservers(null, "places-browser-init-complete");
});
return;
}
let importBookmarks =
!initialMigrationPerformed &&
(dbStatus == lazy.PlacesUtils.history.DATABASE_STATUS_CREATE ||
dbStatus == lazy.PlacesUtils.history.DATABASE_STATUS_CORRUPT);
// Check if user or an extension has required to import bookmarks.html
let importBookmarksHTML = false;
try {
importBookmarksHTML = Services.prefs.getBoolPref(
"browser.places.importBookmarksHTML"
);
if (importBookmarksHTML) {
importBookmarks = true;
}
} catch (ex) {}
// Support legacy bookmarks.html format for apps that depend on that format.
let autoExportHTML = Services.prefs.getBoolPref(
"browser.bookmarks.autoExportHTML",
false
); // Do not export.
if (autoExportHTML) {
// Sqlite.sys.mjs and Places shutdown happen at profile-before-change, thus,
// to be on the safe side, this should run earlier.
lazy.AsyncShutdown.profileChangeTeardown.addBlocker(
"Places: export bookmarks.html",
() =>
lazy.BookmarkHTMLUtils.exportToFile(
lazy.BookmarkHTMLUtils.defaultPath
)
);
}
(async () => {
// Check if Safe Mode or the user has required to restore bookmarks from
// default profile's bookmarks.html
let restoreDefaultBookmarks = false;
try {
restoreDefaultBookmarks = Services.prefs.getBoolPref(
"browser.bookmarks.restore_default_bookmarks"
);
if (restoreDefaultBookmarks) {
// Ensure that we already have a bookmarks backup for today.
await this._backupBookmarks();
importBookmarks = true;
}
} catch (ex) {}
// If the user did not require to restore default bookmarks, or import
// from bookmarks.html, we will try to restore from JSON
if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) {
// get latest JSON backup
let lastBackupFile = await lazy.PlacesBackups.getMostRecentBackup();
if (lastBackupFile) {
// restore from JSON backup
await lazy.BookmarkJSONUtils.importFromFile(lastBackupFile, {
replace: true,
source: lazy.PlacesUtils.bookmarks.SOURCES.RESTORE_ON_STARTUP,
});
importBookmarks = false;
} else {
// We have created a new database but we don't have any backup available
importBookmarks = true;
if (await IOUtils.exists(lazy.BookmarkHTMLUtils.defaultPath)) {
// If bookmarks.html is available in current profile import it...
importBookmarksHTML = true;
} else {
// ...otherwise we will restore defaults
restoreDefaultBookmarks = true;
}
}
}
// Import default bookmarks when necessary.
// Otherwise, if any kind of import runs, default bookmarks creation should be
// delayed till the import operations has finished. Not doing so would
// cause them to be overwritten by the newly imported bookmarks.
if (!importBookmarks) {
// Now apply distribution customized bookmarks.
// This should always run after Places initialization.
try {
await lazy.DistributionManagement.applyBookmarks();
} catch (e) {
console.error(e);
}
} else {
// An import operation is about to run.
let bookmarksUrl = null;
if (restoreDefaultBookmarks) {
// User wants to restore the default set of bookmarks shipped with the
// browser, those that new profiles start with.
bookmarksUrl = "chrome://browser/content/default-bookmarks.html";
} else if (await IOUtils.exists(lazy.BookmarkHTMLUtils.defaultPath)) {
bookmarksUrl = PathUtils.toFileURI(
lazy.BookmarkHTMLUtils.defaultPath
);
}
if (bookmarksUrl) {
// Import from bookmarks.html file.
try {
if (
Services.policies.isAllowed("defaultBookmarks") &&
// Default bookmarks are imported after startup, and they may
// influence the outcome of tests, thus it's possible to use
// this test-only pref to skip the import.
!(
Cu.isInAutomation &&
Services.prefs.getBoolPref(
"browser.bookmarks.testing.skipDefaultBookmarksImport",
false
)
)
) {
await lazy.BookmarkHTMLUtils.importFromURL(bookmarksUrl, {
replace: true,
source: lazy.PlacesUtils.bookmarks.SOURCES.RESTORE_ON_STARTUP,
});
}
} catch (e) {
console.error("Bookmarks.html file could be corrupt. ", e);
}
try {
// Now apply distribution customized bookmarks.
// This should always run after Places initialization.
await lazy.DistributionManagement.applyBookmarks();
} catch (e) {
console.error(e);
}
} else {
console.error(new Error("Unable to find bookmarks.html file."));
}
// Reset preferences, so we won't try to import again at next run
if (importBookmarksHTML) {
Services.prefs.setBoolPref(
"browser.places.importBookmarksHTML",
false
);
}
if (restoreDefaultBookmarks) {
Services.prefs.setBoolPref(
"browser.bookmarks.restore_default_bookmarks",
false
);
}
}
// Initialize bookmark archiving on idle.
// If the last backup has been created before the last browser session,
// and is days old, be more aggressive with the idle timer.
let idleTime = BOOKMARKS_BACKUP_IDLE_TIME_SEC;
if (!(await lazy.PlacesBackups.hasRecentBackup())) {
idleTime /= 2;
}
if (!this._isObservingIdle) {
lazy.UserIdleService.addIdleObserver(this._backupBookmarks, idleTime);
Services.obs.addObserver(this, "profile-before-change");
this._isObservingIdle = true;
}
this._bookmarksBackupIdleTime = idleTime;
if (this._isNewProfile) {
// New profiles may have existing bookmarks (imported from another browser or
// copied into the profile) and we want to show the bookmark toolbar for them
// in some cases.
await lazy.PlacesUIUtils.maybeToggleBookmarkToolbarVisibility();
}
})()
.catch(ex => {
console.error(ex);
})
.then(() => {
// NB: deliberately after the catch so that we always do this, even if
// we threw halfway through initializing in the Task above.
this._placesBrowserInitComplete = true;
Services.obs.notifyObservers(null, "places-browser-init-complete");
});
},
/**
* If a backup for today doesn't exist, this creates one.
*/
async _backupBookmarks() {
let lastBackupFile = await lazy.PlacesBackups.getMostRecentBackup();
// Should backup bookmarks if there are no backups or the maximum
// interval between backups elapsed.
if (
!lastBackupFile ||
new Date() - lazy.PlacesBackups.getDateForFile(lastBackupFile) >
BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS * 86400000
) {
let maxBackups = Services.prefs.getIntPref(
"browser.bookmarks.max_backups"
);
await lazy.PlacesBackups.create(maxBackups);
}
},
/**
* Show the notificationBox for a locked places database.
*/
async _showPlacesLockedNotificationBox() {
var win = lazy.BrowserWindowTracker.getTopWindow();
var buttons = [{ supportPage: "places-locked" }];
var notifyBox = win.gBrowser.getNotificationBox();
var notification = await notifyBox.appendNotification(
"places-locked",
{
label: { "l10n-id": "places-locked-prompt" },
priority: win.gNotificationBox.PRIORITY_CRITICAL_MEDIUM,
},
buttons
);
notification.persistence = -1; // Until user closes it
},
notifyIfInitializationComplete() {
if (this._placesBrowserInitComplete) {
Services.obs.notifyObservers(null, "places-browser-init-complete");
}
},
async maybeAddImportButton() {
// First check if we've already added the import button, in which
// case we should check for events indicating we can remove it.
if (
Services.prefs.getBoolPref("browser.bookmarks.addedImportButton", false)
) {
lazy.PlacesUIUtils.removeImportButtonWhenImportSucceeds();
return;
}
// Otherwise, check if this is a new profile where we need to add it.
// `maybeAddImportButton` will call
// `removeImportButtonWhenImportSucceeds`itself if/when it adds the
// button. Doing things in this order avoids listening for removal
// more than once.
if (
lazy.BrowserHandler.firstRunProfile &&
// Not in automation: the button changes CUI state, breaking tests
!Cu.isInAutomation
) {
await lazy.PlacesUIUtils.maybeAddImportButton();
}
},
handleShutdown() {
if (this._bookmarksBackupIdleTime) {
lazy.UserIdleService.removeIdleObserver(
this._backupBookmarks,
this._bookmarksBackupIdleTime
);
this._bookmarksBackupIdleTime = null;
}
},
observe(subject, topic, _data) {
switch (topic) {
case "profile-before-change":
this.handleShutdown();
break;
}
},
};
PlacesBrowserStartup._backupBookmarks =
PlacesBrowserStartup._backupBookmarks.bind(PlacesBrowserStartup);
PlacesBrowserStartup.QueryInterface = ChromeUtils.generateQI([Ci.nsIObserver]);

View File

@@ -0,0 +1,8 @@
category initial-migration-will-import-default-bookmarks moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs PlacesBrowserStartup.willImportDefaultBookmarks
category initial-migration-did-import-default-bookmarks moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs PlacesBrowserStartup.didImportDefaultBookmarks
# Add import button on first startup
category browser-idle-startup moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs PlacesBrowserStartup.maybeAddImportButton
category browser-first-window-ready moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs PlacesBrowserStartup.onFirstWindowReady
category browser-quit-application-granted moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs PlacesBrowserStartup.handleShutdown

View File

@@ -25,6 +25,14 @@ EXTRA_JS_MODULES += [
"PlacesUIUtils.sys.mjs", "PlacesUIUtils.sys.mjs",
] ]
EXTRA_COMPONENTS += [
"PlacesComponents.manifest",
]
MOZ_SRC_FILES = [
"PlacesBrowserStartup.sys.mjs",
]
FINAL_TARGET_FILES.actors += [ FINAL_TARGET_FILES.actors += [
"InteractionsChild.sys.mjs", "InteractionsChild.sys.mjs",
"InteractionsParent.sys.mjs", "InteractionsParent.sys.mjs",

View File

@@ -25,25 +25,14 @@ async function fetchMenuTree() {
); );
} }
function promiseTopicObserved(aTopic) {
return new Promise(resolve => {
Services.obs.addObserver(function observe(
aObsSubject,
aObsTopic,
aObsData
) {
Services.obs.removeObserver(observe, aObsTopic);
resolve([aObsSubject, aObsData]);
}, aTopic);
});
}
async function simulatePlacesInit() { async function simulatePlacesInit() {
let bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
info("Simulate Places init"); info("Simulate Places init");
// Force nsBrowserGlue::_initPlaces(). let { PlacesBrowserStartup } = ChromeUtils.importESModule(
bg.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT); "moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs"
return promiseTopicObserved("places-browser-init-complete"); );
PlacesBrowserStartup._placesInitialized = false;
PlacesBrowserStartup.initPlaces();
return TestUtils.topicObserved("places-browser-init-complete");
} }
add_task(async function checkDefaultBookmarks() { add_task(async function checkDefaultBookmarks() {

View File

@@ -2,15 +2,13 @@
http://creativecommons.org/publicdomain/zero/1.0/ */ http://creativecommons.org/publicdomain/zero/1.0/ */
/** /**
* Tests that nsBrowserGlue correctly imports bookmarks from distribution.ini. * Tests that distribution correctly imports bookmarks from distribution.ini.
*/ */
const PREF_BMPROCESSED = "distribution.516444.bookmarksProcessed"; const PREF_BMPROCESSED = "distribution.516444.bookmarksProcessed";
const PREF_DISTRIBUTION_ID = "distribution.id"; const PREF_DISTRIBUTION_ID = "distribution.id";
const TOPICDATA_DISTRIBUTION_CUSTOMIZATION = "force-distribution-customization";
const TOPIC_CUSTOMIZATION_COMPLETE = "distribution-customization-complete"; const TOPIC_CUSTOMIZATION_COMPLETE = "distribution-customization-complete";
const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
function run_test() { function run_test() {
// Set special pref to load distribution.ini from the profile folder. // Set special pref to load distribution.ini from the profile folder.
@@ -47,14 +45,13 @@ registerCleanupFunction(function () {
}); });
add_task(async function () { add_task(async function () {
let { DistributionCustomizer } = ChromeUtils.importESModule( let { DistributionManagement } = ChromeUtils.importESModule(
"resource:///modules/distribution.sys.mjs" "resource:///modules/distribution.sys.mjs"
); );
let distribution = new DistributionCustomizer();
let glue = Cc["@mozilla.org/browser/browserglue;1"].getService( // Ensure browser glue is running so it notices places initializing.
Ci.nsIObserver Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
);
// Initialize Places through the History Service and check that a new // Initialize Places through the History Service and check that a new
// database has been created. // database has been created.
Assert.equal( Assert.equal(
@@ -62,11 +59,7 @@ add_task(async function () {
PlacesUtils.history.DATABASE_STATUS_CREATE PlacesUtils.history.DATABASE_STATUS_CREATE
); );
// Force distribution. // Force distribution.
glue.observe( DistributionManagement.applyCustomizations();
null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_DISTRIBUTION_CUSTOMIZATION
);
// Test will continue on customization complete notification. // Test will continue on customization complete notification.
await promiseTopicObserved(TOPIC_CUSTOMIZATION_COMPLETE); await promiseTopicObserved(TOPIC_CUSTOMIZATION_COMPLETE);
@@ -78,7 +71,7 @@ add_task(async function () {
}); });
Assert.equal(menuItem.title, "Menu Link Before"); Assert.equal(menuItem.title, "Menu Link Before");
Assert.ok( Assert.ok(
menuItem.guid.startsWith(distribution.BOOKMARK_GUID_PREFIX), menuItem.guid.startsWith(DistributionManagement.BOOKMARK_GUID_PREFIX),
"Guid of this bookmark has expected prefix" "Guid of this bookmark has expected prefix"
); );
@@ -113,7 +106,7 @@ add_task(async function () {
}); });
Assert.equal(toolbarItem.title, "Toolbar Folder After"); Assert.equal(toolbarItem.title, "Toolbar Folder After");
Assert.ok( Assert.ok(
toolbarItem.guid.startsWith(distribution.FOLDER_GUID_PREFIX), toolbarItem.guid.startsWith(DistributionManagement.FOLDER_GUID_PREFIX),
"Guid of this folder has expected prefix" "Guid of this folder has expected prefix"
); );

View File

@@ -27,10 +27,12 @@ add_task(async function test_migrate_bookmarks() {
PlacesUtils.history.DATABASE_STATUS_CREATE PlacesUtils.history.DATABASE_STATUS_CREATE
); );
// A migrator would run before nsBrowserGlue Places initialization, so mimic // A migrator would run before Places initialization, so mimic
// that behavior adding a bookmark and notifying the migration. // that behavior adding a bookmark and notifying the migration.
let bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver); let { PlacesBrowserStartup } = ChromeUtils.importESModule(
bg.observe(null, "initial-migration-will-import-default-bookmarks", null); "moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs"
);
PlacesBrowserStartup.willImportDefaultBookmarks();
await PlacesUtils.bookmarks.insert({ await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.menuGuid, parentGuid: PlacesUtils.bookmarks.menuGuid,
@@ -41,7 +43,7 @@ add_task(async function test_migrate_bookmarks() {
}); });
let promise = promiseTopicObserved("places-browser-init-complete"); let promise = promiseTopicObserved("places-browser-init-complete");
bg.observe(null, "initial-migration-did-import-default-bookmarks", null); PlacesBrowserStartup.didImportDefaultBookmarks();
await promise; await promise;
// Check the created bookmark still exists. // Check the created bookmark still exists.

View File

@@ -11,10 +11,9 @@ const PREF_RESTORE_DEFAULT_BOOKMARKS =
"browser.bookmarks.restore_default_bookmarks"; "browser.bookmarks.restore_default_bookmarks";
const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML"; const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
const TOPIC_BROWSERGLUE_TEST = "browser-glue-test"; // Make sure browser glue is initialized, so we can initialize places by
const TOPICDATA_FORCE_PLACES_INIT = "test-force-places-init"; // using the history service.
Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
var bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
add_task(async function setup() { add_task(async function setup() {
// Create our bookmarks.html from bookmarks.glue.html. // Create our bookmarks.html from bookmarks.glue.html.
@@ -35,9 +34,12 @@ add_task(async function setup() {
function simulatePlacesInit() { function simulatePlacesInit() {
info("Simulate Places init"); info("Simulate Places init");
// Force nsBrowserGlue::_initPlaces(). let { PlacesBrowserStartup } = ChromeUtils.importESModule(
bg.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT); "moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs"
return promiseTopicObserved("places-browser-init-complete"); );
PlacesBrowserStartup._placesInitialized = false;
PlacesBrowserStartup.initPlaces();
return TestUtils.topicObserved("places-browser-init-complete");
} }
add_task(async function test_checkPreferences() { add_task(async function test_checkPreferences() {

View File

@@ -34,13 +34,12 @@ ChromeUtils.defineESModuleGetters(this, {
var timeInMicroseconds = Date.now() * 1000; var timeInMicroseconds = Date.now() * 1000;
add_task(async function test_execute() { add_task(async function test_execute() {
info("Initialize browserglue before Places"); info("Avoiding full places initialization importing default bookmarks.");
let { PlacesBrowserStartup } = ChromeUtils.importESModule(
// Avoid default bookmarks import. "moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs"
let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(
Ci.nsIObserver
); );
glue.observe(null, "initial-migration-will-import-default-bookmarks", null); PlacesBrowserStartup.willImportDefaultBookmarks();
Sanitizer.onStartup(); Sanitizer.onStartup();
Services.prefs.setBoolPref(Sanitizer.PREF_SHUTDOWN_BRANCH + "cache", true); Services.prefs.setBoolPref(Sanitizer.PREF_SHUTDOWN_BRANCH + "cache", true);

View File

@@ -34,13 +34,12 @@ ChromeUtils.defineESModuleGetters(this, {
var timeInMicroseconds = Date.now() * 1000; var timeInMicroseconds = Date.now() * 1000;
add_task(async function test_execute() { add_task(async function test_execute() {
info("Initialize browserglue before Places"); info("Avoiding full places initialization importing default bookmarks.");
let { PlacesBrowserStartup } = ChromeUtils.importESModule(
// Avoid default bookmarks import. "moz-src:///browser/components/places/PlacesBrowserStartup.sys.mjs"
let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(
Ci.nsIObserver
); );
glue.observe(null, "initial-migration-will-import-default-bookmarks", null); PlacesBrowserStartup.willImportDefaultBookmarks();
Sanitizer.onStartup(); Sanitizer.onStartup();
Services.prefs.setBoolPref(Sanitizer.PREF_SHUTDOWN_BRANCH + "cache", true); Services.prefs.setBoolPref(Sanitizer.PREF_SHUTDOWN_BRANCH + "cache", true);

View File

@@ -5,9 +5,6 @@
* Tests that preferences are properly set by distribution.ini * Tests that preferences are properly set by distribution.ini
*/ */
const TOPICDATA_DISTRIBUTION_CUSTOMIZATION = "force-distribution-customization";
const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
registerCleanupFunction(async function () { registerCleanupFunction(async function () {
// Remove the distribution dir, even if the test failed, otherwise all // Remove the distribution dir, even if the test failed, otherwise all
// next tests will use it. // next tests will use it.
@@ -38,16 +35,12 @@ add_task(async function () {
}); });
add_task(async function () { add_task(async function () {
// Force distribution. let { DistributionManagement } = ChromeUtils.importESModule(
let glue = Cc["@mozilla.org/browser/browserglue;1"].getService( "resource:///modules/distribution.sys.mjs"
Ci.nsIObserver
);
glue.observe(
null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_DISTRIBUTION_CUSTOMIZATION
); );
DistributionManagement.applyCustomizations();
var defaultBranch = Services.prefs.getDefaultBranch(null); var defaultBranch = Services.prefs.getDefaultBranch(null);
Assert.equal(defaultBranch.getCharPref("distribution.id"), "disttest"); Assert.equal(defaultBranch.getCharPref("distribution.id"), "disttest");