summaryrefslogtreecommitdiffstats
path: root/devtools/server/actors/accessibility/parent-accessibility.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/server/actors/accessibility/parent-accessibility.js')
-rw-r--r--devtools/server/actors/accessibility/parent-accessibility.js154
1 files changed, 154 insertions, 0 deletions
diff --git a/devtools/server/actors/accessibility/parent-accessibility.js b/devtools/server/actors/accessibility/parent-accessibility.js
new file mode 100644
index 0000000000..fd2c945ea7
--- /dev/null
+++ b/devtools/server/actors/accessibility/parent-accessibility.js
@@ -0,0 +1,154 @@
+/* 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/. */
+
+"use strict";
+
+const { Actor } = require("resource://devtools/shared/protocol.js");
+const {
+ parentAccessibilitySpec,
+} = require("resource://devtools/shared/specs/accessibility.js");
+
+const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled";
+
+class ParentAccessibilityActor extends Actor {
+ constructor(conn) {
+ super(conn, parentAccessibilitySpec);
+
+ this.userPref = Services.prefs.getIntPref(
+ PREF_ACCESSIBILITY_FORCE_DISABLED
+ );
+
+ if (this.enabled && !this.accService) {
+ // Set a local reference to an accessibility service if accessibility was
+ // started elsewhere to ensure that parent process a11y service does not
+ // get GC'ed away.
+ this.accService = Cc["@mozilla.org/accessibilityService;1"].getService(
+ Ci.nsIAccessibilityService
+ );
+ }
+
+ Services.obs.addObserver(this, "a11y-consumers-changed");
+ Services.prefs.addObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this);
+ }
+
+ bootstrap() {
+ return {
+ canBeDisabled: this.canBeDisabled,
+ canBeEnabled: this.canBeEnabled,
+ };
+ }
+
+ observe(subject, topic, data) {
+ if (topic === "a11y-consumers-changed") {
+ // This event is fired when accessibility service consumers change. Since
+ // this observer lives in parent process there are 2 possible consumers of
+ // a11y service: XPCOM and PlatformAPI (e.g. screen readers). We only care
+ // about PlatformAPI consumer changes because when set, we can no longer
+ // disable accessibility service.
+ const { PlatformAPI } = JSON.parse(data);
+ this.emit("can-be-disabled-change", !PlatformAPI);
+ } else if (
+ !this.disabling &&
+ topic === "nsPref:changed" &&
+ data === PREF_ACCESSIBILITY_FORCE_DISABLED
+ ) {
+ // PREF_ACCESSIBILITY_FORCE_DISABLED preference change event. When set to
+ // >=1, it means that the user wants to disable accessibility service and
+ // prevent it from starting in the future. Note: we also check
+ // this.disabling state when handling this pref change because this is how
+ // we disable the accessibility inspector itself.
+ this.emit("can-be-enabled-change", this.canBeEnabled);
+ }
+ }
+
+ /**
+ * A getter that indicates if accessibility service is enabled.
+ *
+ * @return {Boolean}
+ * True if accessibility service is on.
+ */
+ get enabled() {
+ return Services.appinfo.accessibilityEnabled;
+ }
+
+ /**
+ * A getter that indicates if the accessibility service can be disabled.
+ *
+ * @return {Boolean}
+ * True if accessibility service can be disabled.
+ */
+ get canBeDisabled() {
+ if (this.enabled) {
+ const a11yService = Cc["@mozilla.org/accessibilityService;1"].getService(
+ Ci.nsIAccessibilityService
+ );
+ const { PlatformAPI } = JSON.parse(a11yService.getConsumers());
+ return !PlatformAPI;
+ }
+
+ return true;
+ }
+
+ /**
+ * A getter that indicates if the accessibility service can be enabled.
+ *
+ * @return {Boolean}
+ * True if accessibility service can be enabled.
+ */
+ get canBeEnabled() {
+ return Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED) < 1;
+ }
+
+ /**
+ * Enable accessibility service (via XPCOM service).
+ */
+ enable() {
+ if (this.enabled || !this.canBeEnabled) {
+ return;
+ }
+
+ this.accService = Cc["@mozilla.org/accessibilityService;1"].getService(
+ Ci.nsIAccessibilityService
+ );
+ }
+
+ /**
+ * Force disable accessibility service. This method removes the reference to
+ * the XPCOM a11y service object and flips the
+ * PREF_ACCESSIBILITY_FORCE_DISABLED preference on and off to shutdown a11y
+ * service.
+ */
+ disable() {
+ if (!this.enabled || !this.canBeDisabled) {
+ return;
+ }
+
+ this.disabling = true;
+ this.accService = null;
+ // Set PREF_ACCESSIBILITY_FORCE_DISABLED to 1 to force disable
+ // accessibility service. This is the only way to guarantee an immediate
+ // accessibility service shutdown in all processes. This also prevents
+ // accessibility service from starting up in the future.
+ Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, 1);
+ // Set PREF_ACCESSIBILITY_FORCE_DISABLED back to previous default or user
+ // set value. This will not start accessibility service until the user
+ // activates it again. It simply ensures that accessibility service can
+ // start again (when value is below 1).
+ Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, this.userPref);
+ delete this.disabling;
+ }
+
+ /**
+ * Destroy the helper class, remove all listeners and if possible disable
+ * accessibility service in the parent process.
+ */
+ destroy() {
+ this.disable();
+ super.destroy();
+ Services.obs.removeObserver(this, "a11y-consumers-changed");
+ Services.prefs.removeObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this);
+ }
+}
+
+exports.ParentAccessibilityActor = ParentAccessibilityActor;