mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			381 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			381 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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: "moz-src:///browser/components/places/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]);
 |