parent-accessibility.js (5165B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { Actor } = require("resource://devtools/shared/protocol.js"); 8 const { 9 parentAccessibilitySpec, 10 } = require("resource://devtools/shared/specs/accessibility.js"); 11 12 const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled"; 13 14 class ParentAccessibilityActor extends Actor { 15 constructor(conn) { 16 super(conn, parentAccessibilitySpec); 17 18 this.userPref = Services.prefs.getIntPref( 19 PREF_ACCESSIBILITY_FORCE_DISABLED 20 ); 21 22 if (this.enabled && !this.accService) { 23 // Set a local reference to an accessibility service if accessibility was 24 // started elsewhere to ensure that parent process a11y service does not 25 // get GC'ed away. 26 this.accService = Cc["@mozilla.org/accessibilityService;1"].getService( 27 Ci.nsIAccessibilityService 28 ); 29 } 30 31 Services.obs.addObserver(this, "a11y-consumers-changed"); 32 Services.prefs.addObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this); 33 } 34 35 bootstrap() { 36 return { 37 canBeDisabled: this.canBeDisabled, 38 canBeEnabled: this.canBeEnabled, 39 }; 40 } 41 42 observe(subject, topic, data) { 43 if (topic === "a11y-consumers-changed") { 44 // This event is fired when accessibility service consumers change. Since 45 // this observer lives in parent process there are 2 possible consumers of 46 // a11y service: XPCOM and PlatformAPI (e.g. screen readers). We only care 47 // about PlatformAPI consumer changes because when set, we can no longer 48 // disable accessibility service. 49 const { PlatformAPI } = JSON.parse(data); 50 this.emit("can-be-disabled-change", !PlatformAPI); 51 } else if ( 52 !this.disabling && 53 topic === "nsPref:changed" && 54 data === PREF_ACCESSIBILITY_FORCE_DISABLED 55 ) { 56 // PREF_ACCESSIBILITY_FORCE_DISABLED preference change event. When set to 57 // >=1, it means that the user wants to disable accessibility service and 58 // prevent it from starting in the future. Note: we also check 59 // this.disabling state when handling this pref change because this is how 60 // we disable the accessibility inspector itself. 61 this.emit("can-be-enabled-change", this.canBeEnabled); 62 } 63 } 64 65 /** 66 * A getter that indicates if accessibility service is enabled. 67 * 68 * @return {boolean} 69 * True if accessibility service is on. 70 */ 71 get enabled() { 72 return Services.appinfo.accessibilityEnabled; 73 } 74 75 /** 76 * A getter that indicates if the accessibility service can be disabled. 77 * 78 * @return {boolean} 79 * True if accessibility service can be disabled. 80 */ 81 get canBeDisabled() { 82 if (this.enabled) { 83 const a11yService = Cc["@mozilla.org/accessibilityService;1"].getService( 84 Ci.nsIAccessibilityService 85 ); 86 const { PlatformAPI } = JSON.parse(a11yService.getConsumers()); 87 return !PlatformAPI; 88 } 89 90 return true; 91 } 92 93 /** 94 * A getter that indicates if the accessibility service can be enabled. 95 * 96 * @return {boolean} 97 * True if accessibility service can be enabled. 98 */ 99 get canBeEnabled() { 100 return Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED) < 1; 101 } 102 103 /** 104 * Enable accessibility service (via XPCOM service). 105 */ 106 enable() { 107 if (this.enabled || !this.canBeEnabled) { 108 return; 109 } 110 111 this.accService = Cc["@mozilla.org/accessibilityService;1"].getService( 112 Ci.nsIAccessibilityService 113 ); 114 } 115 116 /** 117 * Force disable accessibility service. This method removes the reference to 118 * the XPCOM a11y service object and flips the 119 * PREF_ACCESSIBILITY_FORCE_DISABLED preference on and off to shutdown a11y 120 * service. 121 */ 122 disable() { 123 if (!this.enabled || !this.canBeDisabled) { 124 return; 125 } 126 127 this.disabling = true; 128 this.accService = null; 129 // Set PREF_ACCESSIBILITY_FORCE_DISABLED to 1 to force disable 130 // accessibility service. This is the only way to guarantee an immediate 131 // accessibility service shutdown in all processes. This also prevents 132 // accessibility service from starting up in the future. 133 Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, 1); 134 // Set PREF_ACCESSIBILITY_FORCE_DISABLED back to previous default or user 135 // set value. This will not start accessibility service until the user 136 // activates it again. It simply ensures that accessibility service can 137 // start again (when value is below 1). 138 Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, this.userPref); 139 delete this.disabling; 140 } 141 142 /** 143 * Destroy the helper class, remove all listeners and if possible disable 144 * accessibility service in the parent process. 145 */ 146 destroy() { 147 this.disable(); 148 super.destroy(); 149 Services.obs.removeObserver(this, "a11y-consumers-changed"); 150 Services.prefs.removeObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this); 151 } 152 } 153 154 exports.ParentAccessibilityActor = ParentAccessibilityActor;