Add InternalKillSwitchWatcher

Bug: twpowertools:162
Change-Id: I693394622e3a1743ffaf80bf3824c0197fb1dd48
diff --git a/src/common/optionsUtils.js b/src/common/optionsUtils.js
index bb0fab0..162fd93 100644
--- a/src/common/optionsUtils.js
+++ b/src/common/optionsUtils.js
@@ -176,3 +176,12 @@
     return options?.[option] === true;
   });
 }
+
+export function getForceDisabledFeatures() {
+  return new Promise((res, rej) => {
+    chrome.storage.sync.get('_forceDisabledFeatures', items => {
+      if (chrome.runtime.lastError) return rej(chrome.runtime.lastError);
+      res(items?.['_forceDisabledFeatures'] ?? []);
+    });
+  });
+}
diff --git a/src/killSwitch/internalKillSwitchWatcher.js b/src/killSwitch/internalKillSwitchWatcher.js
new file mode 100644
index 0000000..86ae5cb
--- /dev/null
+++ b/src/killSwitch/internalKillSwitchWatcher.js
@@ -0,0 +1,46 @@
+import {getForceDisabledFeatures} from '../common/optionsUtils.js';
+
+/**
+ * Watches for changes to an internal kill switch and calls a callback when a
+ * change is detected.
+ */
+export default class InternalKillSwitchWatcher {
+  /**
+   * @param {string} killSwitch - The internal kill switch codename.
+   * @param {changeCallback} callback - The callback which is called when a
+   *     change is detected.
+   * @param {boolean} callbackOnFirstLoad - Whether the callback should be
+   *     called after first loading which is the current state of the kill
+   *     switch.
+   */
+  constructor(killSwitch, callback, callbackOnFirstLoad = true) {
+    this.watchedKillSwitch = killSwitch;
+    this.isActive = null;
+    this.callback = callback;
+    this.#setChangeListener();
+    this.#firstLoadGetValue(callbackOnFirstLoad);
+  }
+
+  #setChangeListener() {
+    chrome.storage.onChanged.addListener((changes, areaName) => {
+      const change = changes?.['_forceDisabledFeatures'];
+      if (areaName !== 'sync' || change === undefined) return;
+      const newValue = change.newValue.includes(this.watchedKillSwitch);
+      const hasChanged = newValue !== this.isActive;
+      this.isActive = newValue;
+      if (hasChanged) this.callback(this.isActive);
+    });
+  }
+
+  async #firstLoadGetValue(callbackOnFirstLoad) {
+    const activeKillSwitches = await getForceDisabledFeatures();
+    this.isActive = activeKillSwitches.includes(this.watchedKillSwitch);
+    if (callbackOnFirstLoad) this.callback(this.isActive);
+  }
+}
+
+/**
+ * Callback which receives changes to the kill switch active state.
+ * @callback changeCallback
+ * @param {boolean} Whether the kill switch is currently active.
+ */