Bug 1312406 - part 1: remove tab groups migration code, r?dolske draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Tue, 01 Nov 2016 15:59:11 +0000
changeset 432307 7cd2a35e7d0fd1726d19b787268541eece471c28
parent 431996 2c773b97167252cedcba0be0c7af9d4cab192ef5
child 432308 edb6c6928f938cede4561b2b5fd13a462a2d3f24
child 432316 fdb866041d749f44053a9ab3f0782a0dc147d882
push id34248
push userbmo:gijskruitbosch+bugs@gmail.com
push dateTue, 01 Nov 2016 16:01:33 +0000
reviewersdolske
bugs1312406
milestone52.0a1
Bug 1312406 - part 1: remove tab groups migration code, r?dolske MozReview-Commit-ID: BDuX607J51w
browser/base/content/aboutTabGroupsMigration.js
browser/base/content/aboutTabGroupsMigration.xhtml
browser/base/jar.mn
browser/components/nsBrowserGlue.js
browser/components/sessionstore/content/aboutSessionRestore.js
browser/locales/en-US/chrome/browser/aboutSessionRestore.dtd
browser/locales/en-US/chrome/browser/browser.properties
browser/modules/TabGroupsMigrator.jsm
browser/modules/moz.build
browser/modules/test/xpcshell/test_TabGroupsMigrator.js
browser/modules/test/xpcshell/xpcshell.ini
deleted file mode 100644
--- a/browser/base/content/aboutTabGroupsMigration.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/* 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/. */
-
-"use strict";
-
-
-Cu.import("resource:///modules/TabGroupsMigrator.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
-  "resource://gre/modules/PlacesUtils.jsm");
-
-const SUPPORT_URL = "https://support.mozilla.org/kb/tab-groups-removal";
-
-function createLink() {
-  let link = document.getElementById("sumolink");
-  link.href = SUPPORT_URL;
-  link.target = "_blank";
-}
-
-let loadPromise = new Promise(resolve => {
-  let loadHandler = e => {
-    window.removeEventListener("DOMContentLoaded", loadHandler);
-    createLink();
-    resolve();
-  };
-  window.addEventListener("DOMContentLoaded", loadHandler, false);
-});
-
-let tabGroupsBookmarkItemId;
-// If the session wasn't restored this run/session, this might be null.
-// Then we shouldn't show the button:
-if (TabGroupsMigrator.bookmarkedGroupsPromise) {
-  let bookmarkPromise = TabGroupsMigrator.bookmarkedGroupsPromise.then(bm => {
-    return PlacesUtils.promiseItemId(bm.guid);
-  }).then(itemId => { tabGroupsBookmarkItemId = itemId });
-
-  Promise.all([bookmarkPromise, loadPromise]).then(function() {
-    document.getElementById("show-migrated-bookmarks-button").style.removeProperty("display");
-  });
-}
-
-function showMigratedGroups() {
-  let browserWin = getBrowserWindow();
-  browserWin.PlacesCommandHook.showPlacesOrganizer(["BookmarksMenu", tabGroupsBookmarkItemId]);
-}
-
deleted file mode 100644
--- a/browser/base/content/aboutTabGroupsMigration.xhtml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-# 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/.
--->
-<!DOCTYPE html [
-  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
-  %htmlDTD;
-  <!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
-  %netErrorDTD;
-  <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
-  %globalDTD;
-  <!ENTITY % restorepageDTD SYSTEM "chrome://browser/locale/aboutSessionRestore.dtd">
-  %restorepageDTD;
-]>
-
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-  <head>
-    <title>&tabgroupsmigration.tabtitle;</title>
-    <link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css" type="text/css" media="all"/>
-    <link rel="stylesheet" href="chrome://browser/skin/aboutSessionRestore.css" type="text/css" media="all"/>
-    <link rel="icon" type="image/png" href="chrome://global/skin/icons/warning-16.png"/>
-
-    <script type="application/javascript;version=1.8" src="chrome://browser/content/aboutSessionRestore.js"/>
-    <script type="application/javascript;version=1.8" src="chrome://browser/content/aboutTabGroupsMigration.js"/>
-  </head>
-
-  <body dir="&locale.dir;">
-
-    <div class="container restore-chosen">
-
-      <div class="title">
-        <h1 class="title-text">&tabgroupsmigration.pagetitle2;</h1>
-      </div>
-      <div class="description">
-        <p id="mainDescription">&tabgroupsmigration.description2;<br /><a id="sumolink">&tabgroupsmigration.learnaboutaddons;</a></p>
-        <button id="show-migrated-bookmarks-button" style="display:none" onclick="showMigratedGroups()">&tabgroupsmigration.bookmarkbutton;</button>
-
-        <p>&tabgroupsmigration.restoredescription;</p>
-      </div>
-      <div class="tree-container" available="true">
-        <xul:tree id="tabList" seltype="single" hidecolumnpicker="true"
-              onclick="onListClick(event);" onkeydown="onListKeyDown(event);"
-              _window_label="&restorepage.windowLabel;">
-          <xul:treecols>
-            <xul:treecol cycler="true" id="restore" type="checkbox" label="&restorepage.restoreHeader;"/>
-            <xul:splitter class="tree-splitter"/>
-            <xul:treecol primary="true" id="title" label="&restorepage.listHeader;" flex="1"/>
-          </xul:treecols>
-          <xul:treechildren flex="1"/>
-        </xul:tree>
-      </div>
-      <div class="button-container">
-#ifdef XP_UNIX
-        <xul:button id="errorCancel"
-                label="&restorepage.closeButton;"
-                accesskey="&restorepage.close.access;"
-                oncommand="startNewSession();"/>
-        <xul:button class="primary"
-                id="errorTryAgain"
-                label="&restorepage.tryagainButton;"
-                accesskey="&restorepage.restore.access;"
-                oncommand="restoreSession();"/>
-#else
-        <xul:button class="primary"
-                id="errorTryAgain"
-                label="&restorepage.tryagainButton;"
-                accesskey="&restorepage.restore.access;"
-                oncommand="restoreSession();"/>
-        <xul:button id="errorCancel"
-                label="&restorepage.closeButton;"
-                accesskey="&restorepage.close.access;"
-                oncommand="startNewSession();"/>
-#endif
-      </div>
-      <!-- holds the session data for when the tab is closed -->
-      <input type="text" id="sessionData" style="display: none;"/>
-    </div>
-
-  </body>
-</html>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -63,18 +63,16 @@ browser.jar:
 
         content/browser/aboutRobots-icon.png          (content/aboutRobots-icon.png)
         content/browser/aboutRobots-widget-left.png   (content/aboutRobots-widget-left.png)
         content/browser/aboutSocialError.xhtml        (content/aboutSocialError.xhtml)
         content/browser/aboutProviderDirectory.xhtml  (content/aboutProviderDirectory.xhtml)
         content/browser/aboutTabCrashed.css           (content/aboutTabCrashed.css)
         content/browser/aboutTabCrashed.js            (content/aboutTabCrashed.js)
         content/browser/aboutTabCrashed.xhtml         (content/aboutTabCrashed.xhtml)
-*       content/browser/aboutTabGroupsMigration.xhtml (content/aboutTabGroupsMigration.xhtml)
-        content/browser/aboutTabGroupsMigration.js    (content/aboutTabGroupsMigration.js)
 *       content/browser/browser.css                   (content/browser.css)
         content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
         content/browser/browser-addons.js             (content/browser-addons.js)
         content/browser/browser-ctrlTab.js            (content/browser-ctrlTab.js)
         content/browser/browser-customization.js      (content/browser-customization.js)
         content/browser/browser-data-submission-info-bar.js (content/browser-data-submission-info-bar.js)
         content/browser/browser-devedition.js         (content/browser-devedition.js)
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -57,17 +57,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
   ["ReaderParent", "resource:///modules/ReaderParent.jsm"],
   ["RecentWindow", "resource:///modules/RecentWindow.jsm"],
   ["RemotePrompt", "resource:///modules/RemotePrompt.jsm"],
   ["SelfSupportBackend", "resource:///modules/SelfSupportBackend.jsm"],
   ["SessionStore", "resource:///modules/sessionstore/SessionStore.jsm"],
   ["ShellService", "resource:///modules/ShellService.jsm"],
   ["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
   ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
-  ["TabGroupsMigrator", "resource:///modules/TabGroupsMigrator.jsm"],
   ["Task", "resource://gre/modules/Task.jsm"],
   ["UITour", "resource:///modules/UITour.jsm"],
   ["URLBarZoom", "resource:///modules/URLBarZoom.jsm"],
   ["WebChannel", "resource://gre/modules/WebChannel.jsm"],
   ["WindowsRegistry", "resource://gre/modules/WindowsRegistry.jsm"],
   ["webrtcUI", "resource:///modules/webrtcUI.jsm"],
 ].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource));
 
@@ -650,19 +649,16 @@ BrowserGlue.prototype = {
     // check if we're in safe mode
     if (Services.appinfo.inSafeMode) {
       // See https://bugzilla.mozilla.org/show_bug.cgi?id=1231112#c7 . We need to
       // register the observer early if we have to migrate tab groups
       let currentUIVersion = 0;
       try {
         currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
       } catch (ex) {}
-      if (currentUIVersion < 35) {
-        this._maybeMigrateTabGroups();
-      }
       Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul",
                              "_blank", "chrome,centerscreen,modal,resizable=no", null);
     }
 
     // apply distribution customizations
     // prefs are applied in _onAppDefaults()
     this._distributionCustomizer.applyCustomizations();
 
@@ -1252,24 +1248,16 @@ BrowserGlue.prototype = {
           return OS.File.makeDir(newProfilePath);
         }).then(null, e => {
           Cu.reportError("Could not empty profile 'default': " + e);
         });
       }
     }
   },
 
-  _maybeMigrateTabGroups() {
-    let migrationObserver = (stateAsSupportsString, topic) => {
-      Services.obs.removeObserver(migrationObserver, "sessionstore-state-read");
-      TabGroupsMigrator.migrate(stateAsSupportsString);
-    };
-    Services.obs.addObserver(migrationObserver, "sessionstore-state-read", false);
-  },
-
   _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
     // If user has already dismissed quit request, then do nothing
     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
       return;
 
     // There are several cases where we won't show a dialog here:
     // 1. There is only 1 tab open in 1 window
     // 2. The session will be restored at startup, indicated by
@@ -2064,20 +2052,17 @@ BrowserGlue.prototype = {
       xulStore.removeValue(BROWSER_DOCURL, "bookmarks-menu-button", "class");
       xulStore.removeValue(BROWSER_DOCURL, "home-button", "class");
     }
 
     if (currentUIVersion < 32) {
       this._notifyNotificationsUpgrade().catch(Cu.reportError);
     }
 
-    // Only do this outside of safe mode, because in safe mode we do this earlier.
-    if (currentUIVersion < 35 && !Services.appinfo.inSafeMode) {
-      this._maybeMigrateTabGroups();
-    }
+    // version 35 migrated tab groups data.
 
     if (currentUIVersion < 36) {
       xulStore.removeValue("chrome://passwordmgr/content/passwordManager.xul",
                            "passwordCol",
                            "hidden");
     }
 
     if (currentUIVersion < 37) {
--- a/browser/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -65,17 +65,17 @@ function initTreeView() {
     return;
   }
   var tabList = document.getElementById("tabList");
   var winLabel = tabList.getAttribute("_window_label");
 
   gTreeData = [];
   gStateObject.windows.forEach(function(aWinData, aIx) {
     var winState = {
-      label: aWinData.tabGroupsMigrationTitle || winLabel.replace("%S", (aIx + 1)),
+      label: winLabel.replace("%S", (aIx + 1)),
       open: true,
       checked: true,
       ix: aIx
     };
     winState.tabs = aWinData.tabs.map(function(aTabData) {
       var entry = aTabData.entries[aTabData.index - 1] || { url: "about:blank" };
       var iconURL = aTabData.image || null;
       // don't initiate a connection just to fetch a favicon (see bug 462863)
--- a/browser/locales/en-US/chrome/browser/aboutSessionRestore.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutSessionRestore.dtd
@@ -44,17 +44,8 @@ before and after the the "learn more" li
 Localizers can use one of them, or both, to better adapt this sentence to
 their language.
 -->
 <!ENTITY welcomeback2.beforelink.pageInfo2  "Your add-ons and customizations have been removed and your browser settings have been restored to their defaults. If this didn’t fix your issue, ">
 <!ENTITY welcomeback2.afterlink.pageInfo2   "">
 
 <!ENTITY welcomeback2.link.pageInfo2        "learn more about what you can do.">
 
-<!-- LOCALIZATION NOTE: The following 'tabgroupsmigration' strings are for
-     the tab groups (panorama) migration page, not about:sessionrestore -->
-<!ENTITY tabgroupsmigration.tabtitle            "Migrate your other Tab Groups">
-<!ENTITY tabgroupsmigration.pagetitle2          "We’ve removed Tab Groups, but saved your tabs">
-<!ENTITY tabgroupsmigration.learnaboutaddons    "Learn about Tab Groups replacement add-ons.">
-<!ENTITY tabgroupsmigration.description2        "&brandShortName; has bookmarked all your groups so you haven’t lost anything.">
-<!ENTITY tabgroupsmigration.bookmarkbutton      "Show Bookmarked Tab Groups">
-<!ENTITY tabgroupsmigration.restoredescription  "You can also choose to restore some or all background groups into windows now:">
-
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -708,21 +708,16 @@ revokeOverride.accesskey = D
 
 # LOCALIZATION NOTE (certErrorDetails*.label): These are text strings that
 # appear in the about:certerror page, so that the user can copy and send them to
 # the server administrators for troubleshooting.
 certErrorDetailsHSTS.label = HTTP Strict Transport Security: %S
 certErrorDetailsKeyPinning.label = HTTP Public Key Pinning: %S
 certErrorDetailsCertChain.label = Certificate chain:
 
-# LOCALIZATION NOTE (tabgroups.migration.anonGroup):
-# %S is the group number/ID
-tabgroups.migration.anonGroup = Group %S
-tabgroups.migration.tabGroupBookmarkFolderName = Bookmarked Tab Groups
-
 # LOCALIZATION NOTE (pendingCrashReports2.label): Semi-colon list of plural forms
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 is the number of pending crash reports
 pendingCrashReports2.label = You have an unsent crash report;You have #1 unsent crash reports
 pendingCrashReports.viewAll = View
 pendingCrashReports.send = Send
 pendingCrashReports.alwaysSend = Always Send
 
deleted file mode 100644
--- a/browser/modules/TabGroupsMigrator.jsm
+++ /dev/null
@@ -1,297 +0,0 @@
-/* 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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["TabGroupsMigrator"];
-
-const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown", "resource://gre/modules/AsyncShutdown.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
-  return Services.strings.createBundle('chrome://browser/locale/browser.properties');
-});
-
-const RECOVERY_URL = "chrome://browser/content/aboutTabGroupsMigration.xhtml";
-
-
-this.TabGroupsMigrator = {
-  bookmarkedGroupsPromise: null,
-
-  /**
-   * If this state contains tab groups, migrate the user's data. This means:
-   * - make a backup of the user's data.
-   * - create bookmarks of all the user's tab groups in a single folder
-   * - append a tab to the active window that lets the user restore background
-   *   groups.
-   * - remove all the tabs hidden through tab groups from the state data.
-   */
-  migrate(stateAsSupportsString) {
-    stateAsSupportsString.QueryInterface(Ci.nsISupportsString);
-    let stateStr = stateAsSupportsString.data;
-    // If this is the very first startup of this profile, this is going to be empty:
-    if (!stateStr) {
-      return;
-    }
-    let state;
-    try {
-      state = JSON.parse(stateStr);
-    } catch (ex) {
-      Cu.reportError("Failed to parse sessionstore state JSON to migrate tab groups: " + ex);
-      return; // can't recover from invalid JSON
-    }
-
-    let groupData = this._gatherGroupData(state);
-
-    // This strips out the hidden tab groups and puts them in a different object.
-    // It also removes all tabview metadata.
-    // We always do this, and always reassign the new state back into the
-    // nsISupportsString for use by sessionstore, in order to tidy up the state
-    // object.
-    let hiddenTabState = this._removeHiddenTabGroupsFromState(state, groupData);
-
-    // However, we will only create a backup file, bookmarks and a new tab to
-    // restore hidden tabs if tabs were actually removed from |state| by
-    // _removeHiddenTabGroupsFromState.
-    if (hiddenTabState.windows.length) {
-      // We create the backup with the original string, from before all our
-      // changes:
-      this._createBackup(stateStr);
-
-      this._createBackgroundTabGroupRestorationPage(state, hiddenTabState);
-
-      // Bookmark creation is async. We need to return synchronously,
-      // so we purposefully don't wait for this to be finished here. We do
-      // store the promise it creates and use that in the session restore page
-      // to be able to link to the bookmarks folder...
-      let bookmarksFinishedPromise = this._bookmarkAllGroupsFromState(groupData);
-      // ... and we make sure we finish before shutting down:
-      AsyncShutdown.profileBeforeChange.addBlocker(
-        "Tab groups migration bookmarks",
-        bookmarksFinishedPromise
-      );
-    }
-
-    // We always write this back to ensure that any spurious tab groups data is
-    // removed:
-    stateAsSupportsString.data = JSON.stringify(state);
-  },
-
-  /**
-   * Returns a Map from window state objects to per-window group data.
-   * Specifically, the values in the Map are themselves Maps from group IDs to
-   * JS Objects which have these properties:
-   *  - tabGroupsMigrationTitle: the title of the group (or empty string)
-   *  - tabs: an array of the tabs objects in this group.
-   */
-  _gatherGroupData(state) {
-    let allGroupData = new Map();
-    let globalAnonGroupID = 0;
-    for (let win of state.windows) {
-      if (win.extData && win.extData["tabview-group"]) {
-        let groupInfo = {};
-        try {
-          groupInfo = JSON.parse(win.extData["tabview-group"]);
-        } catch (ex) {
-          // This is annoying, but we'll try to deal with this.
-        }
-
-        let windowGroupData = new Map();
-        let activeGroupID = null;
-        let tabsWithoutGroup = [];
-        for (let tab of win.tabs) {
-          let group;
-          // Get a string group ID:
-          try {
-            let tabViewData = tab.extData && tab.extData["tabview-tab"] &&
-                              JSON.parse(tab.extData["tabview-tab"]);
-            if (tabViewData && ("groupID" in tabViewData)) {
-              group = tabViewData.groupID + "";
-            }
-          } catch (ex) {
-            // Ignore errors reading group info, treat as active group
-          }
-          if (!group) {
-            // We didn't find group info. If we already have an active group,
-            // pretend this is part of that group:
-            if (activeGroupID) {
-              group = activeGroupID;
-            } else {
-              if (!tabsWithoutGroup) {
-                Cu.reportError("ERROR: the list of tabs without groups was " +
-                               "nulled out, but there's no active group ID? " +
-                               "This should never happen!");
-                tabsWithoutGroup = [];
-              }
-              // Otherwise, add to the list of tabs with no group and move to
-              // the next tab immediately. We'll add all these tabs at the
-              // beginning of the active group as soon as we find a tab in it,
-              // so as to preserve their order.
-              tabsWithoutGroup.push(tab);
-              continue;
-            }
-          }
-          let groupData = windowGroupData.get(group);
-          if (!groupData) {
-            let title = (groupInfo[group] && groupInfo[group].title) || "";
-            groupData = {
-              tabs: [],
-              tabGroupsMigrationTitle: title,
-            };
-            if (!title) {
-              groupData.anonGroupID = ++globalAnonGroupID;
-              groupData.tabGroupsMigrationTitle =
-                gBrowserBundle.formatStringFromName("tabgroups.migration.anonGroup",
-                                                    [groupData.anonGroupID], 1);
-            }
-            // If this is the active group, set the active group ID and add
-            // all the already-known tabs (that didn't list a group ID), if any.
-            if (!activeGroupID && !tab.hidden) {
-              activeGroupID = group;
-              groupData.tabs = tabsWithoutGroup;
-              tabsWithoutGroup = null;
-            }
-            windowGroupData.set(group, groupData);
-          }
-          groupData.tabs.push(tab);
-        }
-
-        // If we found tabs but no active group, assume there's just 1 group:
-        if (tabsWithoutGroup && tabsWithoutGroup.length) {
-          windowGroupData.set("active group", {
-            tabs: tabsWithoutGroup,
-            anonGroupID: ++globalAnonGroupID,
-          });
-        }
-
-        allGroupData.set(win, windowGroupData);
-      }
-    }
-    return allGroupData;
-  },
-
-  _createBackup(stateStr) {
-    let dest = Services.dirsvc.get("ProfD", Ci.nsIFile);
-    dest.append("tabgroups-session-backup.json");
-    let promise = OS.File.writeAtomic(dest.path, stateStr, {encoding: "utf-8"});
-    AsyncShutdown.webWorkersShutdown.addBlocker("TabGroupsMigrator", promise);
-    return promise;
-  },
-
-  _groupSorter(a, b) {
-    if (!a.anonGroupID) {
-      return -1;
-    }
-    if (!b.anonGroupID) {
-      return 1;
-    }
-    return a.anonGroupID - b.anonGroupID;
-  },
-
-  _bookmarkAllGroupsFromState: Task.async(function*(groupData) {
-    // First create a folder in which to put all these bookmarks:
-    this.bookmarkedGroupsPromise = PlacesUtils.bookmarks.insert({
-      parentGuid: PlacesUtils.bookmarks.menuGuid,
-      type: PlacesUtils.bookmarks.TYPE_FOLDER,
-      index: 0,
-      title: gBrowserBundle.GetStringFromName("tabgroups.migration.tabGroupBookmarkFolderName"),
-    }).catch(Cu.reportError);
-    let tabgroupsFolder = yield this.bookmarkedGroupsPromise;
-
-    for (let [, windowGroupMap] of groupData) {
-      let windowGroups = [... windowGroupMap.values()].sort(this._groupSorter);
-      for (let group of windowGroups) {
-        let groupFolder = yield PlacesUtils.bookmarks.insert({
-          parentGuid: tabgroupsFolder.guid,
-          type: PlacesUtils.bookmarks.TYPE_FOLDER,
-          title: group.tabGroupsMigrationTitle
-        }).catch(Cu.reportError);
-
-        for (let tab of group.tabs) {
-          let entry = tab.entries[tab.index - 1];
-          yield PlacesUtils.bookmarks.insert({
-            parentGuid: groupFolder.guid,
-            title: tab.title || entry.title,
-            url: entry.url,
-          }).catch(Cu.reportError);
-        }
-      }
-    }
-  }),
-
-  _removeHiddenTabGroupsFromState(state, groups) {
-    let stateToReturn = {windows: []};
-    for (let win of state.windows) {
-      let groupInfoForWindow = groups.get(win);
-      let hiddenGroupIDs = new Set();
-      for (let i = win.tabs.length - 1; i >= 0; i--) {
-        let tab =  win.tabs[i];
-        // Determine whether the tab is grouped:
-        let tabGroupInfo = null;
-        try {
-          tabGroupInfo = tab.extData && tab.extData["tabview-tab"] &&
-                         JSON.parse(tab.extData["tabview-tab"]);
-        } catch (ex) {}
-
-        // Then remove this data.
-        if (tab.extData) {
-          delete tab.extData["tabview-tab"];
-          if (Object.keys(tab.extData).length == 0) {
-            delete tab.extData;
-          }
-        }
-
-        // If the tab was grouped and hidden, remove it:
-        if (tabGroupInfo && tab.hidden) {
-          hiddenGroupIDs.add(tabGroupInfo.groupID);
-          win.tabs.splice(i, 1);
-          // Make sure we unhide it, or it won't get restored correctly
-          tab.hidden = false;
-        }
-      }
-
-      // We then convert any hidden groups into windows for the state object
-      // we show in about:tabgroupsdata
-      if (groupInfoForWindow) {
-        let windowsToReturn = [];
-        for (let groupID of hiddenGroupIDs) {
-          let group = groupInfoForWindow.get("" + groupID);
-          if (group) {
-            windowsToReturn.push(group);
-          }
-        }
-        windowsToReturn.sort(this._groupSorter);
-        stateToReturn.windows = stateToReturn.windows.concat(windowsToReturn);
-      }
-
-      // Finally we remove tab groups data from the window:
-      if (win.extData) {
-        delete win.extData["tabview-group"];
-        delete win.extData["tabview-groups"];
-        delete win.extData["tabview-ui"];
-        delete win.extData["tabview-visibility"];
-        if (Object.keys(win.extData).length == 0) {
-          delete win.extData;
-        }
-      }
-    }
-    return stateToReturn;
-  },
-
-  _createBackgroundTabGroupRestorationPage(state, backgroundData) {
-    let win = state.windows[(state.selectedWindow || 1) - 1];
-    let formdata = {id: {sessionData: JSON.stringify(backgroundData)}, url: RECOVERY_URL};
-    let newTab = { entries: [{url: RECOVERY_URL}], formdata, index: 1 };
-    // Add tab and mark it as selected:
-    win.selected = win.tabs.push(newTab);
-  },
-};
-
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -39,17 +39,16 @@ EXTRA_JS_MODULES += [
     'ReaderParent.jsm',
     'RecentWindow.jsm',
     'RemotePrompt.jsm',
     'Sanitizer.jsm',
     'SelfSupportBackend.jsm',
     'SitePermissions.jsm',
     'Social.jsm',
     'SocialService.jsm',
-    'TabGroupsMigrator.jsm',
     'TransientPrefs.jsm',
     'URLBarZoom.jsm',
     'webrtcUI.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
         'Windows8WindowFrameColor.jsm',
deleted file mode 100644
--- a/browser/modules/test/xpcshell/test_TabGroupsMigrator.js
+++ /dev/null
@@ -1,406 +0,0 @@
-"use strict";
-
-Components.utils.import("resource://gre/modules/Services.jsm");
-Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
-Components.utils.import("resource://gre/modules/osfile.jsm");
-Components.utils.import("resource:///modules/TabGroupsMigrator.jsm");
-
-var gProfD = do_get_profile();
-
-const TEST_STATES = {
-  TWO_GROUPS: {
-    selectedWindow: 1,
-    windows: [
-      {
-        tabs: [
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 1",
-            }],
-            index: 1,
-            hidden: false,
-            extData: {
-              "tabview-tab": "{\"groupID\":2,\"active\":true}",
-            },
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 2",
-            }],
-            index: 1,
-            hidden: false,
-            extData: {
-              "tabview-tab": "{\"groupID\":2}",
-            },
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 3",
-            }],
-            index: 1,
-            hidden: true,
-            extData: {
-              "tabview-tab": "{\"groupID\":13}",
-            },
-          }
-        ],
-        extData: {
-          "tabview-group": "{\"2\":{},\"13\":{\"title\":\"Foopy\"}}",
-          "tabview-groups": "{\"nextID\":20,\"activeGroupId\":2,\"totalNumber\":2}",
-          "tabview-visibility": "false"
-        },
-      },
-    ]
-  },
-  NAMED_ACTIVE_GROUP: {
-    selectedWindow: 1,
-    windows: [
-      {
-        tabs: [
-          {
-            entries: [{
-              url: "about:mozilla",
-              title: "Mozilla 1",
-            }],
-            index: 1,
-            hidden: false,
-            extData: {
-              "tabview-tab": "{\"groupID\":2,\"active\":true}",
-            },
-          },
-        ],
-        extData: {
-          "tabview-group": "{\"2\":{\"title\":\"Foopy\"}}",
-          "tabview-groups": "{\"nextID\":20,\"activeGroupId\":2,\"totalNumber\":1}",
-          "tabview-visibility": "false"
-        },
-      }
-    ],
-  },
-  TAB_WITHOUT_GROUP: {
-    selectedWindow: 1,
-    windows: [
-      {
-        tabs: [
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 1",
-            }],
-            index: 1,
-            hidden: false,
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 2",
-            }],
-            index: 1,
-            hidden: false,
-            extData: {
-              "tabview-tab": "{\"groupID\":2}",
-            },
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 3",
-            }],
-            index: 1,
-            hidden: true,
-            extData: {
-              "tabview-tab": "{\"groupID\":1}",
-            },
-          }
-        ],
-        extData: {
-          "tabview-group": "{\"2\":{}, \"1\": {}}",
-          "tabview-groups": "{\"nextID\":20,\"activeGroupId\":2,\"totalNumber\":2}",
-          "tabview-visibility": "false"
-        },
-      }
-    ],
-  },
-  ONLY_UNGROUPED_TABS: {
-    selectedWindow: 1,
-    windows: [
-      {
-        tabs: [
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 1",
-            }],
-            index: 1,
-            hidden: false,
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 2",
-            }],
-            index: 1,
-            hidden: false,
-            extData: {
-              "tabview-tab": "{}",
-            },
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 3",
-            }],
-            index: 1,
-            hidden: true,
-            extData: {
-            },
-          }
-        ],
-        extData: {
-          "tabview-group": "{\"2\":{}}",
-        },
-      }
-    ],
-  },
-  SORTING_NAMING_RESTORE_PAGE: {
-    windows: [
-      {
-        tabs: [
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 1",
-            }],
-            index: 1,
-            hidden: false,
-            extData: {
-              "tabview-tab": "{\"groupID\":2,\"active\":true}",
-            },
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 2",
-            }],
-            index: 1,
-            hidden: false,
-            extData: {
-              "tabview-tab": "{\"groupID\":2}",
-            },
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 3",
-            }],
-            index: 1,
-            hidden: true,
-            extData: {
-              "tabview-tab": "{\"groupID\":13}",
-            },
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 4",
-            }],
-            index: 1,
-            hidden: true,
-            extData: {
-              "tabview-tab": "{\"groupID\":15}",
-            },
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 5",
-            }],
-            index: 1,
-            hidden: true,
-            extData: {
-              "tabview-tab": "{\"groupID\":16}",
-            },
-          },
-          {
-            entries: [{
-              url: "about:robots",
-              title: "Robots 6",
-            }],
-            index: 1,
-            hidden: true,
-            extData: {
-              "tabview-tab": "{\"groupID\":17}",
-            },
-          }
-        ],
-        extData: {
-          "tabview-group": "{\"2\":{},\"13\":{\"title\":\"Foopy\"}, \"15\":{\"title\":\"Barry\"}, \"16\":{}, \"17\":{}}",
-          "tabview-groups": "{\"nextID\":20,\"activeGroupId\":2,\"totalNumber\":5}",
-          "tabview-visibility": "false"
-        },
-      },
-    ]
-  },
-};
-
-add_task(function* gatherGroupDataTest() {
-  let groupInfo = TabGroupsMigrator._gatherGroupData(TEST_STATES.TWO_GROUPS);
-  Assert.equal(groupInfo.size, 1, "Information about 1 window");
-  let singleWinGroups = [... groupInfo.values()][0];
-  Assert.equal(singleWinGroups.size, 2, "2 groups");
-  let group2 = singleWinGroups.get("2");
-  Assert.ok(!!group2, "group 2 should exist");
-  Assert.equal(group2.tabs.length, 2, "2 tabs in group 2");
-  // Note that this has groupID 2 in the internal representation of tab groups,
-  // but because it was the first group we encountered when migrating, it was
-  // labeled "group 1" for the user
-  Assert.equal(group2.tabGroupsMigrationTitle, "Group 1", "We assign a numeric title to untitled groups");
-  Assert.equal(group2.anonGroupID, "1", "We mark an untitled group with an anonymous id");
-  let group13 = singleWinGroups.get("13");
-  Assert.ok(!!group13, "group 13 should exist");
-  Assert.equal(group13.tabs.length, 1, "1 tabs in group 13");
-  Assert.equal(group13.tabGroupsMigrationTitle, "Foopy", "Group with title has correct title");
-  Assert.ok(!("anonGroupID" in group13), "We don't mark a titled group with an anonymous id");
-});
-
-add_task(function* bookmarkingTest() {
-  let stateClone = JSON.parse(JSON.stringify(TEST_STATES.TWO_GROUPS));
-  let groupInfo = TabGroupsMigrator._gatherGroupData(stateClone);
-  let removedGroups = TabGroupsMigrator._removeHiddenTabGroupsFromState(stateClone, groupInfo);
-  yield TabGroupsMigrator._bookmarkAllGroupsFromState(groupInfo);
-  let bmCounter = 0;
-  let bmParents = {};
-  let bookmarks = [];
-  let onResult = bm => {
-    bmCounter++;
-    bmParents[bm.parentGuid] = (bmParents[bm.parentGuid] || 0) + 1;
-    Assert.ok(bm.title.startsWith("Robots "), "Bookmark title(" + bm.title + ")  should start with 'Robots '");
-  };
-  yield PlacesUtils.bookmarks.fetch({url: "about:robots"}, onResult);
-  Assert.equal(bmCounter, 3, "Should have seen 3 bookmarks");
-  Assert.equal(Object.keys(bmParents).length, 2, "Should be in 2 folders");
-
-  let ancestorGuid;
-  let parents = Object.keys(bmParents).map(guid => {
-    PlacesUtils.bookmarks.fetch({guid}, bm => {
-      ancestorGuid = bm.parentGuid;
-      if (bmParents[bm.guid] == 1) {
-        Assert.equal(bm.title, "Foopy", "Group with 1 kid has right title");
-      } else {
-        Assert.ok(bm.title.includes("1"), "Group with more kids should have anon ID in title (" + bm.title + ")");
-      }
-    });
-  });
-  yield Promise.all(parents);
-
-  yield PlacesUtils.bookmarks.fetch({guid: ancestorGuid}, bm => {
-    Assert.equal(bm.title,
-      gBrowserBundle.GetStringFromName("tabgroups.migration.tabGroupBookmarkFolderName"),
-      "Should have the right title");
-  });
-});
-
-add_task(function* bookmarkNamedActiveGroup() {
-  let stateClone = JSON.parse(JSON.stringify(TEST_STATES.NAMED_ACTIVE_GROUP));
-  let groupInfo = TabGroupsMigrator._gatherGroupData(stateClone);
-  let removedGroups = TabGroupsMigrator._removeHiddenTabGroupsFromState(stateClone, groupInfo);
-  yield TabGroupsMigrator._bookmarkAllGroupsFromState(groupInfo);
-  let bmParents = {};
-  let bmCounter = 0;
-  let onResult = bm => {
-    bmCounter++;
-    bmParents[bm.parentGuid] = (bmParents[bm.parentGuid] || 0) + 1;
-    Assert.ok(bm.title.startsWith("Mozilla "), "Bookmark title (" + bm.title + ")  should start with 'Mozilla '");
-  };
-  yield PlacesUtils.bookmarks.fetch({url: "about:mozilla"}, onResult);
-  Assert.equal(bmCounter, 1, "Should have seen 1 bookmarks");
-  let parentPromise = PlacesUtils.bookmarks.fetch({guid: Object.keys(bmParents)[0]}, bm => {
-    Assert.equal(bm.title, "Foopy", "Group with 1 kid has right title");
-  });
-  yield parentPromise;
-});
-
-add_task(function* removingTabGroupsFromJSONTest() {
-  let stateClone = JSON.parse(JSON.stringify(TEST_STATES.TWO_GROUPS));
-  let groupInfo = TabGroupsMigrator._gatherGroupData(stateClone);
-  let removedGroups = TabGroupsMigrator._removeHiddenTabGroupsFromState(stateClone, groupInfo);
-  Assert.equal(removedGroups.windows.length, 1, "Removed 1 group which looks like a window in removed data");
-  Assert.equal(removedGroups.windows[0].tabs.length, 1, "Removed group had 1 tab");
-  Assert.ok(!stateClone.windows[0].extData, "extData removed from window");
-  stateClone.windows[0].tabs.forEach(tab => {
-    Assert.ok(!tab.extData, "extData removed from tab");
-  });
-  Assert.ok(stateClone.windows[0].tabs.length, 2, "Only 2 tabs remain in the window");
-});
-
-add_task(function* backupTest() {
-  yield TabGroupsMigrator._createBackup(JSON.stringify(TEST_STATES.TWO_GROUPS));
-  let f = Services.dirsvc.get("ProfD", Components.interfaces.nsIFile);
-  f.append("tabgroups-session-backup.json");
-  ok(f.exists(), "Should have created the file");
-
-  let txt = (new TextDecoder()).decode(yield OS.File.read(f.path));
-  Assert.deepEqual(JSON.parse(txt), TEST_STATES.TWO_GROUPS, "Should have written the expected state.");
-
-  f.remove(false);
-});
-
-add_task(function* migrationPageDataTest() {
-  let stateClone = JSON.parse(JSON.stringify(TEST_STATES.TWO_GROUPS));
-  let groupInfo = TabGroupsMigrator._gatherGroupData(stateClone);
-  let removedGroups = TabGroupsMigrator._removeHiddenTabGroupsFromState(stateClone, groupInfo);
-  TabGroupsMigrator._createBackgroundTabGroupRestorationPage(stateClone, removedGroups);
-  Assert.equal(stateClone.windows.length, 1, "Should still only have 1 window");
-  Assert.equal(stateClone.windows[0].tabs.length, 3, "Should now have 3 tabs");
-
-  let url = "chrome://browser/content/aboutTabGroupsMigration.xhtml";
-  let formdata = {id: {sessionData: JSON.stringify(removedGroups)}, url};
-  Assert.deepEqual(stateClone.windows[0].tabs[2],
-    {
-      entries: [{url}],
-      formdata,
-      index: 1
-    },
-    "Should have added expected tab at the end of the tab list.");
-});
-
-add_task(function* correctMissingTabGroupInfo() {
-  let stateClone = JSON.parse(JSON.stringify(TEST_STATES.TAB_WITHOUT_GROUP));
-  let groupInfo = TabGroupsMigrator._gatherGroupData(stateClone);
-  Assert.equal(groupInfo.size, 1, "Should have 1 window");
-  let windowGroups = [...groupInfo][0][1];
-  Assert.equal(windowGroups.size, 2, "Window should have 2 groups");
-  let group2 = windowGroups.get("2");
-  Assert.ok(group2, "Group 2 should exist");
-  Assert.equal(group2.tabs.length, 2, "There should be 2 tabs in group 2");
-  Assert.equal(group2.tabs[0].entries[0].title, "Robots 1", "The first tab of group 2 should be the tab with no group info.");
-});
-
-add_task(function* dealWithNoGroupInfo() {
-  let stateClone = JSON.parse(JSON.stringify(TEST_STATES.ONLY_UNGROUPED_TABS));
-  let groupInfo = TabGroupsMigrator._gatherGroupData(stateClone);
-  Assert.equal(groupInfo.size, 1, "Should have 1 window");
-  let windowGroups = [...groupInfo][0][1];
-  Assert.equal(windowGroups.size, 1, "Window should have 1 group");
-  let fallbackActiveGroup = windowGroups.get("active group");
-  Assert.ok(fallbackActiveGroup, "Fallback group should exist");
-  Assert.equal(fallbackActiveGroup.tabs.length, 3, "There should be 3 tabs in the fallback group");
-});
-
-add_task(function* groupSortingInRemovedDataUsedForRestorePage() {
-  let stateClone = JSON.parse(JSON.stringify(TEST_STATES.SORTING_NAMING_RESTORE_PAGE));
-  let groupInfo = TabGroupsMigrator._gatherGroupData(stateClone);
-  let removedGroups = TabGroupsMigrator._removeHiddenTabGroupsFromState(stateClone, groupInfo);
-  Assert.equal(stateClone.windows.length, 1, "Should still only have 1 window");
-  Assert.equal(stateClone.windows[0].tabs.length, 2, "Should now have 2 tabs");
-
-  let restoredWindowTitles = removedGroups.windows.map(win => win.tabGroupsMigrationTitle);
-  // Note that group 1 is the active group and as such it won't appear in the list of
-  // things the user can restore:
-  Assert.deepEqual(restoredWindowTitles,
-    ["Barry", "Foopy", "Group 2", "Group 3"]);
-});
-
--- a/browser/modules/test/xpcshell/xpcshell.ini
+++ b/browser/modules/test/xpcshell/xpcshell.ini
@@ -3,10 +3,9 @@ head =
 tail =
 firefox-appdir = browser
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 
 [test_AttributionCode.js]
 skip-if = os != 'win'
 [test_DirectoryLinksProvider.js]
 [test_SitePermissions.js]
-[test_TabGroupsMigrator.js]
 [test_LaterRun.js]