Bug 1213990 Convert getExtensionUUID to UUIDMap r?kmag draft
authorAndrew Swan <aswan@mozilla.com>
Sun, 07 Aug 2016 09:53:36 -0700
changeset 397660 ae206a96200cc93250e9876dcab0a45a7d8340e6
parent 397659 d3e95c24e70ce90ef66a0f6b71dacdb57d5476a7
child 397661 1130e891fdf0acac5791f313ad66ea989e6dab60
push id25345
push useraswan@mozilla.com
push dateSun, 07 Aug 2016 17:04:11 +0000
reviewerskmag
bugs1213990
milestone51.0a1
Bug 1213990 Convert getExtensionUUID to UUIDMap r?kmag MozReview-Commit-ID: 9VVNa0pjx8g
toolkit/components/extensions/Extension.jsm
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -83,16 +83,17 @@ var {
   LocaleData,
   Messenger,
   injectAPI,
   instanceOf,
   flushJarCache,
 } = ExtensionUtils;
 
 const LOGGER_ID_BASE = "addons.webextension.";
+const UUID_MAP_PREF = "extensions.webextensions.uuids";
 
 const COMMENT_REGEXP = new RegExp(String.raw`
     ^
     (
       (?:
         [^"] |
         " (?:[^"\\] | \\.)* "
       )*?
@@ -470,16 +471,70 @@ let ParentAPIManager = {
     let ref = data.path.concat(data.name).join(".");
     let listener = context.listenerProxies.get(ref);
     findPathInObject(context.apiObj, data.path)[data.name].removeListener(listener);
   },
 };
 
 ParentAPIManager.init();
 
+// All moz-extension URIs use a machine-specific UUID rather than the
+// extension's own ID in the host component. This makes it more
+// difficult for web pages to detect whether a user has a given add-on
+// installed (by trying to load a moz-extension URI referring to a
+// web_accessible_resource from the extension). UUIDMap.get()
+// returns the UUID for a given add-on ID.
+var UUIDMap = {
+  _read() {
+    let pref = Preferences.get(UUID_MAP_PREF, "{}");
+    try {
+      return JSON.parse(pref);
+    } catch (e) {
+      Cu.reportError(`Error parsing ${UUID_MAP_PREF}.`);
+      return {};
+    }
+  },
+
+  _write(map) {
+    Preferences.set(UUID_MAP_PREF, JSON.stringify(map));
+  },
+
+  get(id, create = true) {
+    let map = this._read();
+
+    if (id in map) {
+      return map[id];
+    }
+
+    let uuid = null;
+    if (create) {
+      let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
+      uuid = uuidGenerator.generateUUID().number;
+      uuid = uuid.slice(1, -1); // Strip { and } off the UUID.
+
+      map[id] = uuid;
+      this._write(map);
+    }
+    return uuid;
+  },
+
+  remove(id) {
+    let map = this._read();
+    delete map[id];
+    this._write(map);
+  },
+};
+
+// This is the old interface that UUIDMap replaced, to be removed when
+// the references listed in bug 1291399 are updated.
+/* exported getExtensionUUID */
+function getExtensionUUID(id) {
+  return UUIDMap.get(id, true);
+}
+
 // For extensions that have called setUninstallURL(), send an event
 // so the browser can display the URL.
 var UninstallObserver = {
   initialized: false,
 
   init: function() {
     if (!this.initialized) {
       AddonManager.addAddonListener(this);
@@ -703,46 +758,16 @@ GlobalManager = {
       }
 
       contentWindow.console.log(text);
     };
     Cu.exportFunction(alertOverwrite, contentWindow, {defineAs: "alert"});
   },
 };
 
-// All moz-extension URIs use a machine-specific UUID rather than the
-// extension's own ID in the host component. This makes it more
-// difficult for web pages to detect whether a user has a given add-on
-// installed (by trying to load a moz-extension URI referring to a
-// web_accessible_resource from the extension). getExtensionUUID
-// returns the UUID for a given add-on ID.
-function getExtensionUUID(id) {
-  const PREF_NAME = "extensions.webextensions.uuids";
-
-  let pref = Preferences.get(PREF_NAME, "{}");
-  let map = {};
-  try {
-    map = JSON.parse(pref);
-  } catch (e) {
-    Cu.reportError(`Error parsing ${PREF_NAME}.`);
-  }
-
-  if (id in map) {
-    return map[id];
-  }
-
-  let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
-  let uuid = uuidGenerator.generateUUID().number;
-  uuid = uuid.slice(1, -1); // Strip { and } off the UUID.
-
-  map[id] = uuid;
-  Preferences.set(PREF_NAME, JSON.stringify(map));
-  return uuid;
-}
-
 // Represents the data contained in an extension, contained either
 // in a directory or a zip file, which may or may not be installed.
 // This class implements the functionality of the Extension class,
 // primarily related to manifest parsing and localization, which is
 // useful prior to extension installation or initialization.
 //
 // No functionality of this class is guaranteed to work before
 // |readManifest| has been called, and completed.
@@ -793,17 +818,17 @@ this.ExtensionData = class {
    * @param {string} path The path portion of the URL.
    * @returns {string}
    */
   getURL(path = "") {
     if (!(this.id || this.uuid)) {
       throw new Error("getURL may not be called before an `id` or `uuid` has been set");
     }
     if (!this.uuid) {
-      this.uuid = getExtensionUUID(this.id);
+      this.uuid = UUIDMap.get(this.id);
     }
     return `moz-extension://${this.uuid}/${path}`;
   }
 
   readDirectory(path) {
     return Task.spawn(function* () {
       if (this.rootURI instanceof Ci.nsIFileURL) {
         let uri = NetUtil.newURI(this.rootURI.resolve("./" + path));
@@ -1184,17 +1209,17 @@ class MockExtension {
 }
 
 // We create one instance of this class per extension. |addonData|
 // comes directly from bootstrap.js when initializing.
 this.Extension = class extends ExtensionData {
   constructor(addonData) {
     super(addonData.resourceURI);
 
-    this.uuid = getExtensionUUID(addonData.id);
+    this.uuid = UUIDMap.get(addonData.id);
 
     if (addonData.cleanupFile) {
       Services.obs.addObserver(this, "xpcom-shutdown", false);
       this.cleanupFile = addonData.cleanupFile || null;
       delete addonData.cleanupFile;
     }
 
     this.addonData = addonData;