summaryrefslogtreecommitdiffstats
path: root/comm/mail/components/migration/content/migration.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/components/migration/content/migration.js')
-rw-r--r--comm/mail/components/migration/content/migration.js464
1 files changed, 464 insertions, 0 deletions
diff --git a/comm/mail/components/migration/content/migration.js b/comm/mail/components/migration/content/migration.js
new file mode 100644
index 0000000000..10e43600d1
--- /dev/null
+++ b/comm/mail/components/migration/content/migration.js
@@ -0,0 +1,464 @@
+/* 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/. */
+
+var { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.jsm");
+
+var kIMig = Ci.nsIMailProfileMigrator;
+var kIPStartup = Ci.nsIProfileStartup;
+var kProfileMigratorContractIDPrefix =
+ "@mozilla.org/profile/migrator;1?app=mail&type=";
+
+var MigrationWizard = {
+ _source: "", // Source Profile Migrator ContractID suffix
+ _itemsFlags: kIMig.ALL, // Selected Import Data Sources (16-bit bitfield)
+ _selectedProfile: null, // Selected Profile name to import from
+ _wiz: null,
+ _migrator: null,
+ _autoMigrate: null,
+
+ init() {
+ document
+ .querySelector("wizard")
+ .addEventListener("wizardback", this.onBack.bind(this));
+ document
+ .querySelector("wizard")
+ .addEventListener("wizardcancel", this.onCancel.bind(this));
+
+ let importSourcePage = document.getElementById("importSource");
+ importSourcePage.addEventListener(
+ "pageadvanced",
+ this.onImportSourcePageAdvanced.bind(this)
+ );
+
+ let selectProfilePage = document.getElementById("selectProfile");
+ selectProfilePage.addEventListener(
+ "pageshow",
+ this.onSelectProfilePageShow.bind(this)
+ );
+ selectProfilePage.addEventListener(
+ "pagerewound",
+ this.onSelectProfilePageRewound.bind(this)
+ );
+ selectProfilePage.addEventListener(
+ "pageadvanced",
+ this.onSelectProfilePageAdvanced.bind(this)
+ );
+
+ let importItemsPage = document.getElementById("importItems");
+ importItemsPage.addEventListener(
+ "pageshow",
+ this.onImportItemsPageShow.bind(this)
+ );
+ importItemsPage.addEventListener(
+ "pagerewound",
+ this.onImportItemsPageAdvanced.bind(this)
+ );
+ importItemsPage.addEventListener(
+ "pageadvanced",
+ this.onImportItemsPageAdvanced.bind(this)
+ );
+
+ let migratingPage = document.getElementById("migrating");
+ migratingPage.addEventListener(
+ "pageshow",
+ this.onMigratingPageShow.bind(this)
+ );
+
+ let donePage = document.getElementById("done");
+ donePage.addEventListener("pageshow", this.onDonePageShow.bind(this));
+
+ let failedPage = document.getElementById("failed");
+ failedPage.addEventListener("pageshow", () => (this._failed = true));
+ failedPage.addEventListener("pagerewound", () => (this._failed = false));
+
+ Services.obs.addObserver(this, "Migration:Started");
+ Services.obs.addObserver(this, "Migration:ItemBeforeMigrate");
+ Services.obs.addObserver(this, "Migration:ItemAfterMigrate");
+ Services.obs.addObserver(this, "Migration:Ended");
+ Services.obs.addObserver(this, "Migration:Progress");
+
+ this._wiz = document.querySelector("wizard");
+
+ if ("arguments" in window && !window.arguments[3]) {
+ this._source = window.arguments[0];
+ this._migrator = window.arguments[1]
+ ? window.arguments[1].QueryInterface(kIMig)
+ : null;
+ this._autoMigrate = window.arguments[2].QueryInterface(kIPStartup);
+
+ // Show the "nothing" option in the automigrate case to provide an
+ // easily identifiable way to avoid migration and create a new profile.
+ var nothing = document.getElementById("nothing");
+ nothing.hidden = false;
+ }
+
+ this.onImportSourcePageShow();
+
+ // Behavior alert! If we were given a migrator already, then we are going to perform migration
+ // with that migrator, skip the wizard screen where we show all of the migration sources and
+ // jump right into migration.
+ if (this._migrator) {
+ if (this._migrator.sourceHasMultipleProfiles) {
+ this._wiz.goTo("selectProfile");
+ } else {
+ var sourceProfiles = this._migrator.sourceProfiles;
+ this._selectedProfile = sourceProfiles[0];
+ this._wiz.goTo("migrating");
+ }
+ }
+ },
+
+ uninit() {
+ Services.obs.removeObserver(this, "Migration:Started");
+ Services.obs.removeObserver(this, "Migration:ItemBeforeMigrate");
+ Services.obs.removeObserver(this, "Migration:ItemAfterMigrate");
+ Services.obs.removeObserver(this, "Migration:Ended");
+ Services.obs.removeObserver(this, "Migration:Progress");
+
+ // Imported accounts don't show up without restarting.
+ if (this._wiz.onLastPage && !this._failed) {
+ MailUtils.restartApplication();
+ }
+ },
+
+ // 1 - Import Source
+ onImportSourcePageShow() {
+ this._wiz.canRewind = false;
+ this._wiz.canAdvance = false;
+
+ // Figure out what source apps are are available to import from:
+ var group = document.getElementById("importSourceGroup");
+ for (let childNode of group.children) {
+ let suffix = childNode.id;
+ if (suffix != "nothing") {
+ var contractID =
+ kProfileMigratorContractIDPrefix + suffix.split("-")[0];
+ var migrator = Cc[contractID].createInstance(kIMig);
+ if (!migrator.sourceExists) {
+ childNode.hidden = true;
+ if (this._source == suffix) {
+ this._source = null;
+ }
+ }
+ }
+ }
+
+ var firstNonDisabled = null;
+ for (let childNode of group.children) {
+ if (!childNode.hidden && !childNode.disabled) {
+ firstNonDisabled = childNode;
+ break;
+ }
+ }
+ group.selectedItem =
+ this._source == ""
+ ? firstNonDisabled
+ : document.getElementById(this._source);
+
+ if (firstNonDisabled) {
+ this._wiz.canAdvance = true;
+ document.getElementById("importSourceFound").hidden = false;
+ return;
+ }
+ // If no usable import module was found, inform user and enable back button.
+ document.getElementById("importSourceNotFound").hidden = false;
+ this._wiz.canRewind = true;
+ this._wiz.getButton("back").setAttribute("hidden", "false");
+ },
+
+ onImportSourcePageAdvanced() {
+ var newSource =
+ document.getElementById("importSourceGroup").selectedItem.id;
+
+ if (newSource == "nothing") {
+ document.querySelector("wizard").cancel();
+ return;
+ }
+
+ if (!this._migrator || newSource != this._source) {
+ // Create the migrator for the selected source.
+ var contractID =
+ kProfileMigratorContractIDPrefix + newSource.split("-")[0];
+ this._migrator = Cc[contractID].createInstance(kIMig);
+
+ this._itemsFlags = kIMig.ALL;
+ this._selectedProfile = null;
+ }
+
+ this._source = newSource;
+
+ // check for more than one source profile
+ if (this._migrator.sourceHasMultipleProfiles) {
+ this._wiz.currentPage.next = "selectProfile";
+ } else {
+ this._wiz.currentPage.next = "migrating";
+ var sourceProfiles = this._migrator.sourceProfiles;
+ if (sourceProfiles && sourceProfiles.length == 1) {
+ this._selectedProfile = sourceProfiles[0];
+ } else {
+ this._selectedProfile = "";
+ }
+ }
+ },
+
+ // 2 - [Profile Selection]
+ onSelectProfilePageShow() {
+ // Disabling this for now, since we ask about import sources in automigration
+ // too and don't want to disable the back button
+ // if (this._autoMigrate)
+ // document.querySelector("wizard").getButton("back").disabled = true;
+
+ var profiles = document.getElementById("profiles");
+ while (profiles.hasChildNodes()) {
+ profiles.lastChild.remove();
+ }
+
+ if (!this._migrator) {
+ return;
+ }
+ var sourceProfiles = this._migrator.sourceProfiles;
+ var count = sourceProfiles.length;
+ for (var i = 0; i < count; ++i) {
+ var item = document.createXULElement("radio");
+ item.id = sourceProfiles[i];
+ item.setAttribute("label", item.id);
+ profiles.appendChild(item);
+ }
+
+ profiles.selectedItem = this._selectedProfile
+ ? document.getElementById(this._selectedProfile)
+ : profiles.firstElementChild;
+ },
+
+ onSelectProfilePageRewound() {
+ var profiles = document.getElementById("profiles");
+ this._selectedProfile = profiles.selectedItem.id;
+ },
+
+ onSelectProfilePageAdvanced() {
+ var profiles = document.getElementById("profiles");
+ this._selectedProfile = profiles.selectedItem.id;
+
+ // If we're automigrating, don't show the item selection page, just grab everything.
+ if (this._autoMigrate) {
+ this._wiz.currentPage.next = "migrating";
+ }
+ },
+
+ // 3 - ImportItems
+ onImportItemsPageShow() {
+ var dataSources = document.getElementById("dataSources");
+ while (dataSources.hasChildNodes()) {
+ dataSources.lastChild.remove();
+ }
+
+ var bundle = document.getElementById("bundle");
+
+ var items = this._migrator.getMigrateData(
+ this._selectedProfile,
+ this._autoMigrate
+ );
+ for (var i = 0; i < 16; ++i) {
+ var itemID = (items >> i) & 0x1 ? Math.pow(2, i) : 0;
+ if (itemID > 0) {
+ var checkbox = document.createXULElement("checkbox");
+ checkbox.id = itemID;
+ checkbox.setAttribute(
+ "label",
+ bundle.getString(itemID + "_" + this._source.split("-")[0])
+ );
+ dataSources.appendChild(checkbox);
+ if (!this._itemsFlags || this._itemsFlags & itemID) {
+ checkbox.checked = true;
+ }
+ }
+ }
+ },
+
+ onImportItemsPageAdvanced() {
+ var dataSources = document.getElementById("dataSources");
+ this._itemsFlags = 0;
+ for (var i = 0; i < dataSources.children.length; ++i) {
+ var checkbox = dataSources.children[i];
+ if (checkbox.localName == "checkbox" && checkbox.checked) {
+ this._itemsFlags |= parseInt(checkbox.id);
+ }
+ }
+ },
+
+ onImportItemCommand(aEvent) {
+ var items = document.getElementById("dataSources");
+ var checkboxes = items.getElementsByTagName("checkbox");
+
+ var oneChecked = false;
+ for (var i = 0; i < checkboxes.length; ++i) {
+ if (checkboxes[i].checked) {
+ oneChecked = true;
+ break;
+ }
+ }
+
+ this._wiz.canAdvance = oneChecked;
+ },
+
+ // 4 - Migrating
+ async onMigratingPageShow() {
+ this._wiz.getButton("cancel").disabled = true;
+ this._wiz.canRewind = false;
+ this._wiz.canAdvance = false;
+
+ // When automigrating or migrating all, show all of the data that can
+ // be received from this source.
+ if (this._autoMigrate || this._itemsFlags == kIMig.ALL) {
+ this._itemsFlags = this._migrator.getMigrateData(
+ this._selectedProfile,
+ this._autoMigrate
+ );
+ }
+
+ this._listItems("migratingItems");
+ try {
+ await this.onMigratingMigrate();
+ } catch (e) {
+ switch (e.message) {
+ case "file-picker-cancelled":
+ this._wiz.canRewind = true;
+ this._wiz.rewind();
+ this._wiz.canAdvance = true;
+ return;
+ case "zip-file-too-big":
+ this._wiz.canRewind = true;
+ this._wiz.rewind();
+ this._wiz.canAdvance = true;
+ let [zipFileTooBigTitle, zipFileTooBigMessage] =
+ await document.l10n.formatValues([
+ "zip-file-too-big-title",
+ "zip-file-too-big-message",
+ ]);
+ Services.prompt.alert(
+ window,
+ zipFileTooBigTitle,
+ zipFileTooBigMessage
+ );
+ document.getElementById("importSourceGroup").selectedItem =
+ document.getElementById("thunderbird-dir");
+ return;
+ default:
+ document.getElementById("failed-message-default").hidden = e.message;
+ document.getElementById("failed-message").hidden = !e.message;
+ document.getElementById("failed-message").textContent =
+ e.message || "";
+ this._wiz.canAdvance = true;
+ this._wiz.advance("failed");
+ throw e;
+ }
+ }
+ },
+
+ async onMigratingMigrate(aOuter) {
+ let [source, type] = this._source.split("-");
+ if (source == "thunderbird") {
+ // Ask user for the profile directory location.
+ await this._migrator.wrappedJSObject.getProfileDir(window, type);
+ await this._migrator.wrappedJSObject.asyncMigrate();
+ return;
+ }
+ this._migrator.migrate(
+ this._itemsFlags,
+ this._autoMigrate,
+ this._selectedProfile
+ );
+ },
+
+ _listItems(aID) {
+ var items = document.getElementById(aID);
+ while (items.hasChildNodes()) {
+ items.lastChild.remove();
+ }
+
+ var bundle = document.getElementById("bundle");
+ for (var i = 0; i < 16; ++i) {
+ var itemID = (this._itemsFlags >> i) & 0x1 ? Math.pow(2, i) : 0;
+ if (itemID > 0) {
+ var label = document.createXULElement("label");
+ label.id = itemID + "_migrated";
+ try {
+ label.setAttribute(
+ "value",
+ "- " + bundle.getString(itemID + "_" + this._source.split("-")[0])
+ );
+ items.appendChild(label);
+ } catch (e) {
+ // if the block above throws, we've enumerated all the import data types we
+ // currently support and are now just wasting time, break.
+ break;
+ }
+ }
+ }
+ },
+
+ observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "Migration:Started":
+ dump("*** started\n");
+ break;
+ case "Migration:ItemBeforeMigrate": {
+ dump("*** before " + aData + "\n");
+ let label = document.getElementById(aData + "_migrated");
+ if (label) {
+ label.setAttribute("style", "font-weight: bold");
+ }
+ break;
+ }
+ case "Migration:ItemAfterMigrate": {
+ dump("*** after " + aData + "\n");
+ let label = document.getElementById(aData + "_migrated");
+ if (label) {
+ label.removeAttribute("style");
+ }
+ break;
+ }
+ case "Migration:Ended":
+ dump("*** done\n");
+ if (this._autoMigrate) {
+ // We're done now.
+ this._wiz.canAdvance = true;
+ this._wiz.advance();
+ setTimeout(window.close, 5000);
+ } else {
+ this._wiz.canAdvance = true;
+ var nextButton = this._wiz.getButton("next");
+ nextButton.click();
+ }
+ break;
+ case "Migration:Progress":
+ document.getElementById("progressBar").value = aData;
+ break;
+ }
+ },
+
+ onDonePageShow() {
+ this._wiz.getButton("cancel").disabled = true;
+ this._wiz.canRewind = false;
+ this._listItems("doneItems");
+ },
+
+ onBack(event) {
+ this._wiz.goTo("importSource");
+ this._wiz.canRewind = false;
+ event.preventDefault();
+ },
+
+ onCancel() {
+ // If .closeMigration is false, the user clicked Back button,
+ // then do not change its value.
+ if (
+ window.arguments[3] &&
+ "closeMigration" in window.arguments[3] &&
+ window.arguments[3].closeMigration !== false
+ ) {
+ window.arguments[3].closeMigration = true;
+ }
+ },
+};