Bug 1432605: Fix alarms API when multiple contexts use it. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Tue, 23 Jan 2018 18:08:00 -0800
changeset 723892 8be1bc0924c58eb493421920c20b1fba7deaa172
parent 723422 a12c47f485e3d661fc693be3113ad79ea186be64
child 746989 e8131288cc9cff65213f1d5e75dfdf2131f707b8
push id96572
push usermaglione.k@gmail.com
push dateWed, 24 Jan 2018 02:11:19 +0000
reviewersaswan
bugs1432605
milestone60.0a1
Bug 1432605: Fix alarms API when multiple contexts use it. r?aswan MozReview-Commit-ID: E5CIYrlEiSd
toolkit/components/extensions/ext-alarms.js
--- a/toolkit/components/extensions/ext-alarms.js
+++ b/toolkit/components/extensions/ext-alarms.js
@@ -1,22 +1,16 @@
 "use strict";
 
 // The ext-* files are imported into the same scopes.
 /* import-globals-from ext-toolkit.js */
 
-// WeakMap[Extension -> Map[name -> Alarm]]
-let alarmsMap = new WeakMap();
-
-// WeakMap[Extension -> Set[callback]]
-let alarmCallbacksMap = new WeakMap();
-
 // Manages an alarm created by the extension (alarms API).
-function Alarm(extension, name, alarmInfo) {
-  this.extension = extension;
+function Alarm(api, name, alarmInfo) {
+  this.api = api;
   this.name = name;
   this.when = alarmInfo.when;
   this.delayInMinutes = alarmInfo.delayInMinutes;
   this.periodInMinutes = alarmInfo.periodInMinutes;
   this.canceled = false;
 
   let delay, scheduledTime;
   if (this.when) {
@@ -35,26 +29,26 @@ function Alarm(extension, name, alarmInf
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT);
   this.timer = timer;
 }
 
 Alarm.prototype = {
   clear() {
     this.timer.cancel();
-    alarmsMap.get(this.extension).delete(this.name);
+    this.api.alarms.delete(this.name);
     this.canceled = true;
   },
 
   observe(subject, topic, data) {
     if (this.canceled) {
       return;
     }
 
-    for (let callback of alarmCallbacksMap.get(this.extension)) {
+    for (let callback of this.api.callbacks) {
       callback(this);
     }
 
     if (!this.periodInMinutes) {
       this.clear();
       return;
     }
 
@@ -68,85 +62,80 @@ Alarm.prototype = {
       name: this.name,
       scheduledTime: this.scheduledTime,
       periodInMinutes: this.periodInMinutes,
     };
   },
 };
 
 this.alarms = class extends ExtensionAPI {
-  onShutdown() {
-    let {extension} = this;
+  constructor(extension) {
+    super(extension);
 
-    if (alarmsMap.has(extension)) {
-      for (let alarm of alarmsMap.get(extension).values()) {
-        alarm.clear();
-      }
-      alarmsMap.delete(extension);
-      alarmCallbacksMap.delete(extension);
+    this.alarms = new Map();
+    this.callbacks = new Set();
+  }
+
+  onShutdown() {
+    for (let alarm of this.alarms.values()) {
+      alarm.clear();
     }
   }
 
   getAPI(context) {
-    let {extension} = context;
-
-    alarmsMap.set(extension, new Map());
-    alarmCallbacksMap.set(extension, new Set());
+    const self = this;
 
     return {
       alarms: {
         create: function(name, alarmInfo) {
           name = name || "";
-          let alarms = alarmsMap.get(extension);
-          if (alarms.has(name)) {
-            alarms.get(name).clear();
+          if (self.alarms.has(name)) {
+            self.alarms.get(name).clear();
           }
-          let alarm = new Alarm(extension, name, alarmInfo);
-          alarms.set(alarm.name, alarm);
+          let alarm = new Alarm(self, name, alarmInfo);
+          self.alarms.set(alarm.name, alarm);
         },
 
         get: function(name) {
           name = name || "";
-          let alarms = alarmsMap.get(extension);
-          if (alarms.has(name)) {
-            return Promise.resolve(alarms.get(name).data);
+          if (self.alarms.has(name)) {
+            return Promise.resolve(self.alarms.get(name).data);
           }
           return Promise.resolve();
         },
 
         getAll: function() {
-          let result = Array.from(alarmsMap.get(extension).values(), alarm => alarm.data);
+          let result = Array.from(self.alarms.values(), alarm => alarm.data);
           return Promise.resolve(result);
         },
 
         clear: function(name) {
           name = name || "";
-          let alarms = alarmsMap.get(extension);
-          if (alarms.has(name)) {
-            alarms.get(name).clear();
+          if (self.alarms.has(name)) {
+            self.alarms.get(name).clear();
             return Promise.resolve(true);
           }
           return Promise.resolve(false);
         },
 
         clearAll: function() {
           let cleared = false;
-          for (let alarm of alarmsMap.get(extension).values()) {
+          for (let alarm of self.alarms.values()) {
             alarm.clear();
             cleared = true;
           }
           return Promise.resolve(cleared);
         },
 
         onAlarm: new EventManager(context, "alarms.onAlarm", fire => {
           let callback = alarm => {
             fire.sync(alarm.data);
           };
 
-          alarmCallbacksMap.get(extension).add(callback);
+          self.callbacks.add(callback);
           return () => {
-            alarmCallbacksMap.get(extension).delete(callback);
+            self.callbacks.delete(callback);
           };
         }).api(),
       },
     };
   }
 };