Bug 1448920 - add support for importing Edge typed URL history from its database instead, r=mak
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Wed, 18 Apr 2018 17:50:36 +0100
changeset 468218 1f04da36cb83f3db03c1629135fd71678fc2b4d6
parent 468217 67c3eb947f52620b9b5d53d5562233bf75f23983
child 468219 db3d3eb4b46c52ee285a235c54dfc0041d092b27
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1448920
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1448920 - add support for importing Edge typed URL history from its database instead, r=mak MozReview-Commit-ID: 6AzEJluqn73
browser/components/migration/ESEDBReader.jsm
browser/components/migration/EdgeProfileMigrator.js
--- a/browser/components/migration/ESEDBReader.jsm
+++ b/browser/components/migration/ESEDBReader.jsm
@@ -113,16 +113,18 @@ function convertESEError(errorCode) {
     case -1002 /* JET_errInvalidName*/:
     case -1507 /* JET_errColumnNotFound */:
       // The DB format has changed and we haven't updated this migration code:
       return "The database format has changed, error code: " + errorCode;
     case -1032 /* JET_errFileAccessDenied */:
     case -1207 /* JET_errDatabaseLocked */:
     case -1302 /* JET_errTableLocked */:
       return "The database or table is locked, error code: " + errorCode;
+    case -1305 /* JET_errObjectNotFound */:
+      return "The table/object was not found.";
     case -1809 /* JET_errPermissionDenied*/:
     case -1907 /* JET_errAccessDenied */:
       return "Access or permission denied, error code: " + errorCode;
     case -1044 /* JET_errInvalidFilename */:
       return "Invalid file name";
     case -1811 /* JET_errFileNotFound */:
       return "File not found";
     case -550 /* JET_errDatabaseDirtyShutdown */:
--- a/browser/components/migration/EdgeProfileMigrator.js
+++ b/browser/components/migration/EdgeProfileMigrator.js
@@ -47,38 +47,38 @@ XPCOMUtils.defineLazyGetter(this, "gEdge
 /**
  * Get rows from a table in the Edge DB as an array of JS objects.
  *
  * @param {String}            tableName the name of the table to read.
  * @param {String[]|function} columns   a list of column specifiers
  *                                      (see ESEDBReader.jsm) or a function that
  *                                      generates them based on the database
  *                                      reference once opened.
- * @param {function}          filterFn  a function that is called for each row.
+ * @param {nsIFile}           dbFile    the database file to use. Defaults to
+ *                                      the main Edge database.
+ * @param {function}          filterFn  Optional. A function that is called for each row.
  *                                      Only rows for which it returns a truthy
  *                                      value are included in the result.
- * @param {nsIFile}           dbFile    the database file to use. Defaults to
- *                                      the main Edge database.
  * @returns {Array} An array of row objects.
  */
-function readTableFromEdgeDB(tableName, columns, filterFn, dbFile = gEdgeDatabase) {
+function readTableFromEdgeDB(tableName, columns, dbFile = gEdgeDatabase, filterFn = null) {
   let database;
   let rows = [];
   try {
     let logFile = dbFile.parent;
     logFile.append("LogFiles");
     database = ESEDBReader.openDB(dbFile.parent, dbFile, logFile);
 
     if (typeof columns == "function") {
       columns = columns(database);
     }
 
     let tableReader = database.tableItems(tableName, columns);
     for (let row of tableReader) {
-      if (filterFn(row)) {
+      if (!filterFn || filterFn(row)) {
         rows.push(row);
       }
     }
   } catch (ex) {
     Cu.reportError("Failed to extract items from table " + tableName + " in Edge database at " +
                    dbFile.path + " due to the following error: " + ex);
     // Deliberately make this fail so we expose failure in the UI:
     throw ex;
@@ -109,17 +109,17 @@ EdgeTypedURLMigrator.prototype = {
 
   migrate(aCallback) {
     let typedURLs = this._typedURLs;
     let pageInfos = [];
     for (let [urlString, time] of typedURLs) {
       let url;
       try {
         url = new URL(urlString);
-        if (!["http", "https", "ftp"].includes(url.scheme)) {
+        if (!["http:", "https:", "ftp:"].includes(url.protocol)) {
           continue;
         }
       } catch (ex) {
         Cu.reportError(ex);
         continue;
       }
 
       pageInfos.push({
@@ -137,16 +137,96 @@ EdgeTypedURLMigrator.prototype = {
     }
 
     MigrationUtils.insertVisitsWrapper(pageInfos).then(
       () => aCallback(true),
       () => aCallback(false));
   },
 };
 
+function EdgeTypedURLDBMigrator() {
+}
+
+EdgeTypedURLDBMigrator.prototype = {
+  type: MigrationUtils.resourceTypes.HISTORY,
+
+  get db() { return gEdgeDatabase; },
+
+  get exists() {
+    return !!this.db;
+  },
+
+  migrate(callback) {
+    this._migrateTypedURLsFromDB().then(
+      () => callback(true),
+      ex => {
+        Cu.reportError(ex);
+        callback(false);
+      }
+    );
+  },
+
+  async _migrateTypedURLsFromDB() {
+    if (await ESEDBReader.dbLocked(this.db)) {
+      throw new Error("Edge seems to be running - its database is locked.");
+    }
+    let columns = [
+      {name: "URL", type: "string"},
+      {name: "AccessDateTimeUTC", type: "date"},
+    ];
+
+    let typedUrls = [];
+    try {
+      typedUrls = readTableFromEdgeDB("TypedUrls", columns, this.db);
+    } catch (ex) {
+      // Maybe the table doesn't exist (older versions of Win10).
+      // Just fall through and we'll return because there's no data.
+      // The `readTableFromEdgeDB` helper will report errors to the
+      // console anyway.
+    }
+    if (!typedUrls.length) {
+      return;
+    }
+
+    let pageInfos = [];
+    // Sometimes the values are bogus (e.g. 0 becomes some date in 1600),
+    // and places will throw *everything* away, not just the bogus ones,
+    // so deal with that by having a cutoff date. Also, there's not much
+    // point importing really old entries. The cut-off date is related to
+    // Edge's launch date.
+    const kDateCutOff = new Date("2016", 0, 1);
+    for (let typedUrlInfo of typedUrls) {
+      try {
+        let url = new URL(typedUrlInfo.URL);
+        if (!["http:", "https:", "ftp:"].includes(url.protocol)) {
+          continue;
+        }
+
+        let date = typedUrlInfo.AccessDateTimeUTC;
+        if (!date) {
+          date = kDateCutOff;
+        } else if (date < kDateCutOff) {
+          continue;
+        }
+
+        pageInfos.push({
+          url,
+          visits: [{
+            transition: PlacesUtils.history.TRANSITIONS.TYPED,
+            date,
+          }],
+        });
+      } catch (ex) {
+        Cu.reportError(ex);
+      }
+    }
+    await MigrationUtils.insertVisitsWrapper(pageInfos);
+  },
+};
+
 function EdgeReadingListMigrator(dbOverride) {
   this.dbOverride = dbOverride;
 }
 
 EdgeReadingListMigrator.prototype = {
   type: MigrationUtils.resourceTypes.BOOKMARKS,
 
   get db() { return this.dbOverride || gEdgeDatabase; },
@@ -183,17 +263,17 @@ EdgeReadingListMigrator.prototype = {
       }
       return columns;
     };
 
     let filterFn = row => {
       return !row.IsDeleted;
     };
 
-    let readingListItems = readTableFromEdgeDB("ReadingList", columnFn, filterFn, this.db);
+    let readingListItems = readTableFromEdgeDB("ReadingList", columnFn, this.db, filterFn);
     if (!readingListItems.length) {
       return;
     }
 
     let destFolderGuid = await this._ensureReadingListFolder(parentGuid);
     let bookmarks = [];
     for (let item of readingListItems) {
       let dateAdded = item.AddedDate || new Date();
@@ -282,17 +362,17 @@ EdgeBookmarksMigrator.prototype = {
       if (row.IsDeleted) {
         return false;
       }
       if (row.IsFolder) {
         folderMap.set(row.ItemId, row);
       }
       return true;
     };
-    let bookmarks = readTableFromEdgeDB(this.TABLE_NAME, columns, filterFn, this.db);
+    let bookmarks = readTableFromEdgeDB(this.TABLE_NAME, columns, this.db, filterFn);
     let toplevelBMs = [], toolbarBMs = [];
     for (let bookmark of bookmarks) {
       let bmToInsert;
       // Ignore invalid URLs:
       if (!bookmark.IsFolder) {
         try {
           new URL(bookmark.URL);
         } catch (ex) {
@@ -352,16 +432,17 @@ EdgeProfileMigrator.prototype.getReading
   return new EdgeReadingListMigrator(dbOverride);
 };
 
 EdgeProfileMigrator.prototype.getResources = function() {
   let resources = [
     new EdgeBookmarksMigrator(),
     MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
     new EdgeTypedURLMigrator(),
+    new EdgeTypedURLDBMigrator(),
     new EdgeReadingListMigrator(),
   ];
   let windowsVaultFormPasswordsMigrator =
     MSMigrationUtils.getWindowsVaultFormPasswordsMigrator();
   windowsVaultFormPasswordsMigrator.name = "EdgeVaultFormPasswords";
   resources.push(windowsVaultFormPasswordsMigrator);
   return resources.filter(r => r.exists);
 };