Bug 1334252 - Find IDB storage for host more directly. r=jdescottes a=gchang
authorJ. Ryan Stinnett <jryans@gmail.com>
Tue, 14 Feb 2017 12:13:01 -0600
changeset 378581 a4dd9746a71219c06fb2b178c4938d90f1ac4317
parent 378580 6e46b6455cae726e8a791f5b321a9ce262916feb
child 378582 661e35b83224d2962961d955c834d4c08ce8320d
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes, gchang
bugs1334252, 1276339
milestone53.0a2
Bug 1334252 - Find IDB storage for host more directly. r=jdescottes a=gchang After bug 1276339, storage inspector started iterating over all directories in the profile's storage to find IDB data for a host. This approach is too general and can go through thousands of directories in a large profile. By using more of what we know about the host and storage layout, we can pinpoint the directories of interest more quickly. MozReview-Commit-ID: 563GNMX1Ges
devtools/server/actors/storage.js
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals StopIteration */
-
 "use strict";
 
 const {Cc, Ci, Cu, CC} = require("chrome");
 const events = require("sdk/event/core");
 const protocol = require("devtools/shared/protocol");
 const {LongStringActor} = require("devtools/server/actors/string");
 const {DebuggerServer} = require("devtools/server/main");
 const Services = require("Services");
@@ -1757,17 +1755,19 @@ StorageActors.createActor({
       [host]: [ JSON.stringify(path) ]
     });
   },
 
   maybeSetupChildProcess() {
     if (!DebuggerServer.isInChildProcess) {
       this.backToChild = (func, rv) => rv;
       this.clearDBStore = indexedDBHelpers.clearDBStore;
-      this.gatherFilesOrFolders = indexedDBHelpers.gatherFilesOrFolders;
+      this.findIDBPathsForHost = indexedDBHelpers.findIDBPathsForHost;
+      this.findSqlitePathsForHost = indexedDBHelpers.findSqlitePathsForHost;
+      this.findStorageTypePaths = indexedDBHelpers.findStorageTypePaths;
       this.getDBMetaData = indexedDBHelpers.getDBMetaData;
       this.getDBNamesForHost = indexedDBHelpers.getDBNamesForHost;
       this.getNameFromDatabaseFile = indexedDBHelpers.getNameFromDatabaseFile;
       this.getObjectStoreData = indexedDBHelpers.getObjectStoreData;
       this.getSanitizedHost = indexedDBHelpers.getSanitizedHost;
       this.getValuesForHost = indexedDBHelpers.getValuesForHost;
       this.openWithPrincipal = indexedDBHelpers.openWithPrincipal;
       this.removeDB = indexedDBHelpers.removeDB;
@@ -2034,45 +2034,33 @@ var indexedDBHelpers = {
 
     // We expect sqlite DB paths to look something like this:
     // - PathToProfileDir/storage/default/http+++www.example.com/
     //   idb/1556056096MeysDaabta.sqlite
     // - PathToProfileDir/storage/permanent/http+++www.example.com/
     //   idb/1556056096MeysDaabta.sqlite
     // - PathToProfileDir/storage/temporary/http+++www.example.com/
     //   idb/1556056096MeysDaabta.sqlite
-    //
     // The subdirectory inside the storage folder is determined by the storage
     // type:
     // - default:   { storage: "default" } or not specified.
     // - permanent: { storage: "persistent" }.
     // - temporary: { storage: "temporary" }.
-    let sqliteFiles = yield this.gatherFilesOrFolders(storagePath, path => {
-      if (path.endsWith(".sqlite")) {
-        let { components } = OS.Path.split(path);
-        let isIDB = components[components.length - 2] === "idb";
-
-        return isIDB;
-      }
-      return false;
-    });
+    let sqliteFiles = yield this.findSqlitePathsForHost(storagePath, sanitizedHost);
 
     for (let file of sqliteFiles) {
       let splitPath = OS.Path.split(file).components;
       let idbIndex = splitPath.indexOf("idb");
-      let name = splitPath[idbIndex - 1];
       let storage = splitPath[idbIndex - 2];
       let relative = file.substr(profileDir.length + 1);
 
-      if (name.startsWith(sanitizedHost)) {
-        files.push({
-          file: relative,
-          storage: storage === "permanent" ? "persistent" : storage
-        });
-      }
+      files.push({
+        file: relative,
+        storage: storage === "permanent" ? "persistent" : storage
+      });
     }
 
     if (files.length > 0) {
       for (let {file, storage} of files) {
         let name = yield this.getNameFromDatabaseFile(file);
         if (name) {
           names.push({
             name,
@@ -2081,58 +2069,65 @@ var indexedDBHelpers = {
         }
       }
     }
 
     return this.backToChild("getDBNamesForHost", {names});
   }),
 
   /**
-   * Gather together all of the files in path and pass each path through a
-   * validation function.
-   *
-   * @param {String}
-   *        Path in which to begin searching.
-   * @param {Function}
-   *        Validation function, which checks each file path. If this function
-   *        Returns true the file path is kept.
-   *
-   * @returns {Array}
-   *          An array of file paths.
+   * Find all SQLite files that hold IndexedDB data for a host, such as:
+   *   storage/temporary/http+++www.example.com/idb/1556056096MeysDaabta.sqlite
    */
-  gatherFilesOrFolders: Task.async(function* (path, validationFunc) {
-    let files = [];
-    let iterator;
-    let paths = [path];
+  findSqlitePathsForHost: Task.async(function* (storagePath, sanitizedHost) {
+    let sqlitePaths = [];
+    let idbPaths = yield this.findIDBPathsForHost(storagePath, sanitizedHost);
+    for (let idbPath of idbPaths) {
+      let iterator = new OS.File.DirectoryIterator(idbPath);
+      yield iterator.forEach(entry => {
+        if (!entry.isDir && entry.path.endsWith(".sqlite")) {
+          sqlitePaths.push(entry.path);
+        }
+      });
+      iterator.close();
+    }
+    return sqlitePaths;
+  }),
 
-    while (paths.length > 0) {
-      try {
-        iterator = new OS.File.DirectoryIterator(paths.pop());
-
-        for (let child in iterator) {
-          child = yield child;
-
-          path = child.path;
-
-          if (child.isDir) {
-            paths.push(path);
-          } else if (validationFunc(path)) {
-            files.push(path);
-          }
-        }
-      } catch (ex) {
-        // Ignore StopIteration to prevent exiting the loop.
-        if (ex != StopIteration) {
-          throw ex;
-        }
+  /**
+   * Find all paths that hold IndexedDB data for a host, such as:
+   *   storage/temporary/http+++www.example.com/idb
+   */
+  findIDBPathsForHost: Task.async(function* (storagePath, sanitizedHost) {
+    let idbPaths = [];
+    let typePaths = yield this.findStorageTypePaths(storagePath);
+    for (let typePath of typePaths) {
+      let idbPath = OS.Path.join(typePath, sanitizedHost, "idb");
+      if (yield OS.File.exists(idbPath)) {
+        idbPaths.push(idbPath);
       }
     }
-    iterator.close();
+    return idbPaths;
+  }),
 
-    return files;
+  /**
+   * Find all the storage types, such as "default", "permanent", or "temporary".
+   * These names have changed over time, so it seems simpler to look through all types
+   * that currently exist in the profile.
+   */
+  findStorageTypePaths: Task.async(function* (storagePath) {
+    let iterator = new OS.File.DirectoryIterator(storagePath);
+    let typePaths = [];
+    yield iterator.forEach(entry => {
+      if (entry.isDir) {
+        typePaths.push(entry.path);
+      }
+    });
+    iterator.close();
+    return typePaths;
   }),
 
   /**
    * Removes any illegal characters from the host name to make it a valid file
    * name.
    */
   getSanitizedHost(host) {
     if (host.startsWith("about:")) {