diff --git a/devtools/client/accessibility/accessibility-proxy.js b/devtools/client/accessibility/accessibility-proxy.js index 056f575e0f8f..6c5dbb67cbb8 100644 --- a/devtools/client/accessibility/accessibility-proxy.js +++ b/devtools/client/accessibility/accessibility-proxy.js @@ -9,6 +9,11 @@ const { } = require("devtools/shared/constants"); const { FILTERS } = require("devtools/client/accessibility/constants"); +const PARENT_ACCESSIBILITY_EVENTS = [ + "can-be-disabled-change", + "can-be-enabled-change", +]; + /** * Component responsible for tracking all Accessibility fronts in parent and * content processes. @@ -100,12 +105,34 @@ class AccessibilityProxy { this.accessibleWalkerFront.off("picker-accessible-canceled", onCanceled); } - disableAccessibility() { - return this.accessibilityFront.disable(); + async disableAccessibility() { + // Accessibility service is shut down using the parent accessibility front. + // That, in turn, shuts down accessibility service in all content processes. + // We need to wait until that happens to be sure platform accessibility is + // fully disabled. + // TODO: Remove this after Firefox 75 and use parentAccessibilityFront. + if (this.parentAccessibilityFront) { + const disabled = this.accessibilityFront.once("shutdown"); + await this.parentAccessibilityFront.disable(); + await disabled; + } else { + await this.accessibilityFront.disable(); + } } - enableAccessibility() { - return this.accessibilityFront.enable(); + async enableAccessibility() { + // Accessibility service is initialized using the parent accessibility + // front. That, in turn, initializes accessibility service in all content + // processes. We need to wait until that happens to be sure platform + // accessibility is fully enabled. + // TODO: Remove this after Firefox 75 and use parentAccessibilityFront. + if (this.parentAccessibilityFront) { + const enabled = this.accessibilityFront.once("init"); + await this.parentAccessibilityFront.enable(); + await enabled; + } else { + await this.accessibilityFront.enable(); + } } /** @@ -133,7 +160,9 @@ class AccessibilityProxy { } async resetAccessiblity() { - const { enabled, canBeDisabled, canBeEnabled } = this.accessibilityFront; + const { enabled } = this.accessibilityFront; + const { canBeEnabled, canBeDisabled } = + this.parentAccessibilityFront || this.accessibilityFront; return { enabled, canBeDisabled, canBeEnabled }; } @@ -152,8 +181,14 @@ class AccessibilityProxy { startListeningForLifecycleEvents(eventMap) { for (let [type, listeners] of Object.entries(eventMap)) { listeners = Array.isArray(listeners) ? listeners : [listeners]; + const accessibilityFront = + // TODO: Remove parentAccessibilityFront check after Firefox 75. + this.parentAccessibilityFront && + PARENT_ACCESSIBILITY_EVENTS.includes(type) + ? this.parentAccessibilityFront + : this.accessibilityFront; for (const listener of listeners) { - this.accessibilityFront.on(type, listener); + accessibilityFront.on(type, listener); } } } @@ -161,24 +196,39 @@ class AccessibilityProxy { stopListeningForLifecycleEvents(eventMap) { for (let [type, listeners] of Object.entries(eventMap)) { listeners = Array.isArray(listeners) ? listeners : [listeners]; + // TODO: Remove parentAccessibilityFront check after Firefox 75. + const accessibilityFront = + this.parentAccessibilityFront && + PARENT_ACCESSIBILITY_EVENTS.includes(type) + ? this.parentAccessibilityFront + : this.accessibilityFront; for (const listener of listeners) { - this.accessibilityFront.off(type, listener); + accessibilityFront.off(type, listener); } } } + async ensureReady() { + const { mainRoot } = this.target.client; + if (await mainRoot.hasActor("parentAccessibility")) { + this.parentAccessibilityFront = await mainRoot.getFront( + "parentaccessibility" + ); + } + + this.accessibleWalkerFront = this.accessibilityFront.accessibleWalkerFront; + this.simulatorFront = this.accessibilityFront.simulatorFront; + if (this.simulatorFront) { + this.simulate = types => this.simulatorFront.simulate({ types }); + } + } + async initialize() { try { this.accessibilityFront = await this.target.getFront("accessibility"); // Finalize accessibility front initialization. See accessibility front // bootstrap method description. await this.accessibilityFront.bootstrap(); - this.accessibleWalkerFront = this.accessibilityFront.accessibleWalkerFront; - this.simulatorFront = this.accessibilityFront.simulatorFront; - if (this.simulatorFront) { - this.simulate = types => this.simulatorFront.simulate({ types }); - } - this.supports = {}; // To add a check for backward compatibility add something similar to the // example below: @@ -196,6 +246,7 @@ class AccessibilityProxy { destroy() { this.accessibilityFront = null; + this.parentAccessibilityFront = null; this.accessibleWalkerFront = null; this.simulatorFront = null; this.simulate = null; diff --git a/devtools/client/accessibility/panel.js b/devtools/client/accessibility/panel.js index ddce74a57080..f99d0fc908a0 100644 --- a/devtools/client/accessibility/panel.js +++ b/devtools/client/accessibility/panel.js @@ -90,6 +90,7 @@ AccessibilityPanel.prototype = { this.shouldRefresh = true; await this.startup.initAccessibility(); + await this.accessibilityProxy.ensureReady(); this.picker = new Picker(this); this.fluentBundles = await this.createFluentBundles(); diff --git a/devtools/server/actors/accessibility/accessibility.js b/devtools/server/actors/accessibility/accessibility.js index 226e8a041c4b..b49dd365279b 100644 --- a/devtools/server/actors/accessibility/accessibility.js +++ b/devtools/server/actors/accessibility/accessibility.js @@ -4,10 +4,8 @@ "use strict"; -const { DevToolsServer } = require("devtools/server/devtools-server"); const Services = require("Services"); const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol"); -const defer = require("devtools/shared/defer"); const { accessibilitySpec } = require("devtools/shared/specs/accessibility"); loader.lazyRequireGetter( @@ -22,7 +20,6 @@ loader.lazyRequireGetter( "devtools/server/actors/accessibility/simulator", true ); -loader.lazyRequireGetter(this, "events", "devtools/shared/event-emitter"); loader.lazyRequireGetter( this, "isWebRenderEnabled", @@ -30,189 +27,35 @@ loader.lazyRequireGetter( true ); -const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled"; - /** * The AccessibilityActor is a top level container actor that initializes * accessible walker and is the top-most point of interaction for accessibility - * tools UI. + * tools UI for a top level content process. */ const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, { initialize(conn, targetActor) { Actor.prototype.initialize.call(this, conn); - - this.initializedDeferred = defer(); - - if (DevToolsServer.isInChildProcess) { - this._msgName = `debug:${this.conn.prefix}accessibility`; - // eslint-disable-next-line no-restricted-properties - this.conn.setupInParent({ - module: "devtools/server/actors/accessibility/accessibility-parent", - setupParent: "setupParentProcess", - }); - - this.onMessage = this.onMessage.bind(this); - this.messageManager.addMessageListener( - `${this._msgName}:event`, - this.onMessage - ); - } else { - this.userPref = Services.prefs.getIntPref( - PREF_ACCESSIBILITY_FORCE_DISABLED - ); - Services.obs.addObserver(this, "a11y-consumers-changed"); - Services.prefs.addObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this); - this.initializedDeferred.resolve(); - } - + // This event is fired when accessibility service is initialized or shut + // down. "init" and "shutdown" events are only relayed when the enabled + // state matches the event (e.g. the event came from the same process as + // the actor). Services.obs.addObserver(this, "a11y-init-or-shutdown"); this.targetActor = targetActor; }, bootstrap() { - return this.initializedDeferred.promise.then(() => ({ + return { enabled: this.enabled, - canBeEnabled: this.canBeEnabled, - canBeDisabled: this.canBeDisabled, - })); + }; }, get enabled() { return Services.appinfo.accessibilityEnabled; }, - get canBeEnabled() { - if (DevToolsServer.isInChildProcess) { - return this._canBeEnabled; - } - - return Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED) < 1; - }, - - get canBeDisabled() { - if (DevToolsServer.isInChildProcess) { - return this._canBeDisabled; - } else if (!this.enabled) { - return true; - } - - const { PlatformAPI } = JSON.parse(this.walker.a11yService.getConsumers()); - return !PlatformAPI; - }, - - /** - * Getter for a message manager that corresponds to a current tab. It is onyl - * used if the AccessibilityActor runs in the child process. - * - * @return {Object} - * Message manager that corresponds to the current content tab. - */ - get messageManager() { - if (!DevToolsServer.isInChildProcess) { - throw new Error( - "Message manager should only be used when actor is in child process." - ); - } - - return this.conn.parentMessageManager; - }, - - onMessage(msg) { - const { topic, data } = msg.data; - - switch (topic) { - case "initialized": - this._canBeEnabled = data.canBeEnabled; - this._canBeDisabled = data.canBeDisabled; - - // Sometimes when the tool is reopened content process accessibility service is - // not shut down yet because GC did not run in that process (though it did in - // parent process and the service was shut down there). We need to sync the two - // services if possible. - if (!data.enabled && this.enabled && data.canBeEnabled) { - this.messageManager.sendAsyncMessage(this._msgName, { - action: "enable", - }); - } - - this.initializedDeferred.resolve(); - break; - case "can-be-disabled-change": - this._canBeDisabled = data; - events.emit(this, "can-be-disabled-change", this.canBeDisabled); - break; - - case "can-be-enabled-change": - this._canBeEnabled = data; - events.emit(this, "can-be-enabled-change", this.canBeEnabled); - break; - - default: - break; - } - }, - - /** - * Enable acessibility service in the given process. - */ - async enable() { - if (this.enabled || !this.canBeEnabled) { - return; - } - - const initPromise = this.once("init"); - - if (DevToolsServer.isInChildProcess) { - this.messageManager.sendAsyncMessage(this._msgName, { action: "enable" }); - } else { - // This executes accessibility service lazy getter and adds accessible - // events observer. - this.walker.a11yService; - } - - await initPromise; - }, - - /** - * Disable acessibility service in the given process. - */ - async disable() { - if (!this.enabled || !this.canBeDisabled) { - return; - } - - this.disabling = true; - const shutdownPromise = this.once("shutdown"); - if (DevToolsServer.isInChildProcess) { - this.messageManager.sendAsyncMessage(this._msgName, { - action: "disable", - }); - } else { - // 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. - // - // TODO: Introduce a shutdown method that is exposed via XPCOM on - // accessibility service. - 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 - ); - } - - await shutdownPromise; - delete this.disabling; - }, - /** * Observe Accessibility service init and shutdown events. It relays these - * events to AccessibilityFront iff the event is fired for the a11y service + * events to AccessibilityFront if the event is fired for the a11y service * that lives in the same process. * * @param {null} subject @@ -223,41 +66,15 @@ const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, { * "0" corresponds to shutdown and "1" to init. */ observe(subject, topic, data) { - if (topic === "a11y-init-or-shutdown") { - // This event is fired when accessibility service is initialized or shut - // down. "init" and "shutdown" events are only relayed when the enabled - // state matches the event (e.g. the event came from the same process as - // the actor). - const enabled = data === "1"; - if (enabled && this.enabled) { - events.emit(this, "init"); - } else if (!enabled && !this.enabled) { - if (this.walker) { - this.walker.reset(); - } - - events.emit(this, "shutdown"); + const enabled = data === "1"; + if (enabled && this.enabled) { + this.emit("init"); + } else if (!enabled && !this.enabled) { + if (this.walker) { + this.walker.reset(); } - } else if (topic === "a11y-consumers-changed") { - // This event is fired when accessibility service consumers change. There - // are 3 possible consumers of a11y service: XPCOM, PlatformAPI (e.g. - // screen readers) and MainProcess. PlatformAPI consumer can only be set - // in parent process, and MainProcess consumer can only be set in child - // process. We only care about PlatformAPI consumer changes because when - // set, we can no longer disable accessibility service. - const { PlatformAPI } = JSON.parse(data); - events.emit(this, "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. - events.emit(this, "can-be-enabled-change", this.canBeEnabled); + + this.emit("shutdown"); } }, @@ -277,9 +94,10 @@ const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, { /** * Get or create Simulator actor, managed by AccessibilityActor, - * only if webrender is enabled. Simulator applies color filters on an entire viewport. - * This needs to be done using webrender and not an SVG since it is - * accelerated and scrolling with filter applied needs to be smooth (Bug1431466). + * only if webrender is enabled. Simulator applies color filters on an entire + * viewport. This needs to be done using webrender and not an SVG + * since it is accelerated and scrolling with filter applied + * needs to be smooth (Bug1431466). * * @return {Object|null} * SimulatorActor for the current tab. @@ -299,35 +117,14 @@ const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, { }, /** - * Destroy accessibility service actor. This method also shutsdown - * accessibility service if possible. + * Destroy accessibility actor. This method also shutsdown accessibility + * service if possible. */ async destroy() { - if (this.destroyed) { - await this.destroyed; - return; - } - - let resolver; - this.destroyed = new Promise(resolve => { - resolver = resolve; - }); - - Services.obs.removeObserver(this, "a11y-init-or-shutdown"); - if (DevToolsServer.isInChildProcess) { - this.messageManager.removeMessageListener( - `${this._msgName}:event`, - this.onMessage - ); - } else { - Services.obs.removeObserver(this, "a11y-consumers-changed"); - Services.prefs.removeObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this); - } - Actor.prototype.destroy.call(this); + Services.obs.removeObserver(this, "a11y-init-or-shutdown"); this.walker = null; this.targetActor = null; - resolver(); }, }); diff --git a/devtools/server/actors/accessibility/moz.build b/devtools/server/actors/accessibility/moz.build index 6c9afa15fb85..c686118c3aa7 100644 --- a/devtools/server/actors/accessibility/moz.build +++ b/devtools/server/actors/accessibility/moz.build @@ -7,10 +7,10 @@ DIRS += [ ] DevToolsModules( - 'accessibility-parent.js', 'accessibility.js', 'accessible.js', 'constants.js', + 'parent-accessibility.js', 'simulator.js', 'walker.js', 'worker.js', diff --git a/devtools/server/actors/accessibility/accessibility-parent.js b/devtools/server/actors/accessibility/parent-accessibility.js similarity index 59% rename from devtools/server/actors/accessibility/accessibility-parent.js rename to devtools/server/actors/accessibility/parent-accessibility.js index 8730d5571179..dd314d9bf76a 100644 --- a/devtools/server/actors/accessibility/accessibility-parent.js +++ b/devtools/server/actors/accessibility/parent-accessibility.js @@ -6,26 +6,20 @@ const { Cc, Ci } = require("chrome"); const Services = require("Services"); +const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol"); +const { + parentAccessibilitySpec, +} = require("devtools/shared/specs/accessibility"); + const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled"; -/** - * A helper class that does all the work related to accessibility service - * lifecycle (initialization, shutdown, consumer changes, etc) in parent - * parent process. It is not guaranteed that the AccessibilityActor starts in - * parent process and thus triggering these lifecycle functions directly is - * extremely unreliable. - */ -class AccessibilityParent { - constructor(mm, prefix) { - this._msgName = `debug:${prefix}accessibility`; - this.onAccessibilityMessage = this.onAccessibilityMessage.bind(this); - this.setMessageManager(mm); +const ParentAccessibilityActor = ActorClassWithSpec(parentAccessibilitySpec, { + initialize(conn) { + Actor.prototype.initialize.call(this, conn); this.userPref = Services.prefs.getIntPref( PREF_ACCESSIBILITY_FORCE_DISABLED ); - Services.obs.addObserver(this, "a11y-consumers-changed"); - Services.prefs.addObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this); if (this.enabled && !this.accService) { // Set a local reference to an accessibility service if accessibility was @@ -36,65 +30,16 @@ class AccessibilityParent { ); } - this.messageManager.sendAsyncMessage(`${this._msgName}:event`, { - topic: "initialized", - data: { - enabled: this.enabled, - canBeDisabled: this.canBeDisabled, - canBeEnabled: this.canBeEnabled, - }, - }); - } + Services.obs.addObserver(this, "a11y-consumers-changed"); + Services.prefs.addObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this); + }, - /** - * Set up message manager listener to listen for messages coming from the - * AccessibilityActor when it is instantiated in the child process. - * - * @param {Object} mm - * Message manager that corresponds to the current content tab. - */ - setMessageManager(mm) { - if (this.messageManager === mm) { - return; - } - - if (this.messageManager) { - // If the browser was swapped we need to reset the message manager. - const oldMM = this.messageManager; - oldMM.removeMessageListener(this._msgName, this.onAccessibilityMessage); - } - - this.messageManager = mm; - if (mm) { - mm.addMessageListener(this._msgName, this.onAccessibilityMessage); - } - } - - /** - * Content AccessibilityActor message listener. - * - * @param {String} msg - * Name of the action to perform. - */ - onAccessibilityMessage(msg) { - const { action } = msg.json; - switch (action) { - case "enable": - this.enable(); - break; - - case "disable": - this.disable(); - break; - - case "disconnect": - this.destroy(); - break; - - default: - break; - } - } + bootstrap() { + return { + canBeDisabled: this.canBeDisabled, + canBeEnabled: this.canBeEnabled, + }; + }, observe(subject, topic, data) { if (topic === "a11y-consumers-changed") { @@ -104,10 +49,7 @@ class AccessibilityParent { // about PlatformAPI consumer changes because when set, we can no longer // disable accessibility service. const { PlatformAPI } = JSON.parse(data); - this.messageManager.sendAsyncMessage(`${this._msgName}:event`, { - topic: "can-be-disabled-change", - data: !PlatformAPI, - }); + this.emit("can-be-disabled-change", !PlatformAPI); } else if ( !this.disabling && topic === "nsPref:changed" && @@ -118,12 +60,9 @@ class AccessibilityParent { // 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.messageManager.sendAsyncMessage(`${this._msgName}:event`, { - topic: "can-be-enabled-change", - data: Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED) < 1, - }); + this.emit("can-be-enabled-change", this.canBeEnabled); } - } + }, /** * A getter that indicates if accessibility service is enabled. @@ -133,7 +72,7 @@ class AccessibilityParent { */ get enabled() { return Services.appinfo.accessibilityEnabled; - } + }, /** * A getter that indicates if the accessibility service can be disabled. @@ -151,7 +90,7 @@ class AccessibilityParent { } return true; - } + }, /** * A getter that indicates if the accessibility service can be enabled. @@ -161,7 +100,7 @@ class AccessibilityParent { */ get canBeEnabled() { return Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED) < 1; - } + }, /** * Enable accessibility service (via XPCOM service). @@ -174,7 +113,7 @@ class AccessibilityParent { this.accService = Cc["@mozilla.org/accessibilityService;1"].getService( Ci.nsIAccessibilityService ); - } + }, /** * Force disable accessibility service. This method removes the reference to @@ -200,42 +139,18 @@ class AccessibilityParent { // start again (when value is below 1). Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, this.userPref); delete this.disabling; - } + }, /** * Destroy thie helper class, remove all listeners and if possible disable * accessibility service in the parent process. */ destroy() { + Actor.prototype.destroy.call(this); Services.obs.removeObserver(this, "a11y-consumers-changed"); Services.prefs.removeObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this); - this.setMessageManager(null); this.accService = null; - } -} + }, +}); -/** - * Setup function that runs in parent process and setups AccessibleActor bits - * that must always run in parent process. - * - * @param {Object} options.mm - * Message manager that corresponds to the current content tab. - * @param {String} options.prefix - * Unique prefix for message manager messages. - * @return {Object} - * Defines event listeners for when client disconnects or browser gets - * swapped. - */ -function setupParentProcess({ mm, prefix }) { - let accessibility = new AccessibilityParent(mm, prefix); - - return { - onBrowserSwap: newMM => accessibility.setMessageManager(newMM), - onDisconnected: () => { - accessibility.destroy(); - accessibility = null; - }, - }; -} - -exports.setupParentProcess = setupParentProcess; +exports.ParentAccessibilityActor = ParentAccessibilityActor; diff --git a/devtools/server/actors/utils/actor-registry.js b/devtools/server/actors/utils/actor-registry.js index de9907ccd949..40b750a16e69 100644 --- a/devtools/server/actors/utils/actor-registry.js +++ b/devtools/server/actors/utils/actor-registry.js @@ -156,6 +156,19 @@ const ActorRegistry = { constructor: "PerfActor", type: { global: true }, }); + /** + * Always register parent accessibility actor as a global module. This + * actor is responsible for all top level accessibility actor functionality + * that relies on the parent process. + */ + this.registerModule( + "devtools/server/actors/accessibility/parent-accessibility", + { + prefix: "parentAccessibility", + constructor: "ParentAccessibilityActor", + type: { global: true }, + } + ); }, /** diff --git a/devtools/shared/fronts/accessibility.js b/devtools/shared/fronts/accessibility.js index 976e52e0515e..d892d5f5d726 100644 --- a/devtools/shared/fronts/accessibility.js +++ b/devtools/shared/fronts/accessibility.js @@ -12,6 +12,7 @@ const { accessibleSpec, accessibleWalkerSpec, accessibilitySpec, + parentAccessibilitySpec, simulatorSpec, } = require("devtools/shared/specs/accessibility"); const events = require("devtools/shared/event-emitter"); @@ -180,10 +181,14 @@ class AccessibilityFront extends FrontClassWithSpec(accessibilitySpec) { this.before("init", this.init.bind(this)); this.before("shutdown", this.shutdown.bind(this)); + + // TODO: Deprecated. Remove after Fx75. this.before("can-be-enabled-change", this.canBeEnabled.bind(this)); + // TODO: Deprecated. Remove after Fx75. this.before("can-be-disabled-change", this.canBeDisabled.bind(this)); - // Attribute name from which to retrieve the actorID out of the target actor's form + // Attribute name from which to retrieve the actorID out of the target + // actor's form this.formAttributeName = "accessibilityActor"; } @@ -200,6 +205,7 @@ class AccessibilityFront extends FrontClassWithSpec(accessibilitySpec) { async bootstrap() { this.accessibleWalkerFront = await super.getWalker(); this.simulatorFront = await super.getSimulator(); + // TODO: Deprecated. Remove canBeEnabled and canBeDisabled after Fx75. ({ enabled: this.enabled, canBeEnabled: this.canBeEnabled, @@ -215,6 +221,37 @@ class AccessibilityFront extends FrontClassWithSpec(accessibilitySpec) { this.enabled = false; } + // TODO: Deprecated. Remove after Fx75. + canBeEnabled(canBeEnabled) { + this.canBeEnabled = canBeEnabled; + } + + // TODO: Deprecated. Remove after Fx75. + canBeDisabled(canBeDisabled) { + this.canBeDisabled = canBeDisabled; + } +} + +class ParentAccessibilityFront extends FrontClassWithSpec( + parentAccessibilitySpec +) { + constructor(client, targetFront, parentFront) { + super(client, targetFront, parentFront); + this.before("can-be-enabled-change", this.canBeEnabled.bind(this)); + this.before("can-be-disabled-change", this.canBeDisabled.bind(this)); + + // Attribute name from which to retrieve the actorID out of the target + // actor's form + this.formAttributeName = "parentAccessibilityActor"; + } + + async initialize() { + ({ + canBeEnabled: this.canBeEnabled, + canBeDisabled: this.canBeDisabled, + } = await super.bootstrap()); + } + canBeEnabled(canBeEnabled) { this.canBeEnabled = canBeEnabled; } @@ -232,5 +269,7 @@ exports.AccessibleWalkerFront = AccessibleWalkerFront; registerFront(AccessibleWalkerFront); exports.AccessibilityFront = AccessibilityFront; registerFront(AccessibilityFront); +exports.ParentAccessibilityFront = ParentAccessibilityFront; +registerFront(ParentAccessibilityFront); exports.SimulatorFront = SimulatorFront; registerFront(SimulatorFront); diff --git a/devtools/shared/fronts/root.js b/devtools/shared/fronts/root.js index ba87af95fa9b..48ac9b066966 100644 --- a/devtools/shared/fronts/root.js +++ b/devtools/shared/fronts/root.js @@ -414,6 +414,19 @@ class RootFront extends FrontClassWithSpec(rootSpec) { this.fronts.set(typeName, front); return front; } + + /* + * This function returns true if the root actor has a registered global actor + * with a given name. + * @param {String} actorName + * The name of a global actor. + * + * @return {Boolean} + */ + async hasActor(actorName) { + const rootForm = await this.rootForm; + return !!rootForm[actorName + "Actor"]; + } } exports.RootFront = RootFront; registerFront(RootFront); diff --git a/devtools/shared/specs/accessibility.js b/devtools/shared/specs/accessibility.js index e9c1914124c0..1fec8eab968e 100644 --- a/devtools/shared/specs/accessibility.js +++ b/devtools/shared/specs/accessibility.js @@ -222,10 +222,12 @@ const accessibilitySpec = generateActorSpec({ shutdown: { type: "shutdown", }, + // TODO: Deprecated. Remove after Fx75. "can-be-disabled-change": { type: "canBeDisabledChange", canBeDisabled: Arg(0, "boolean"), }, + // TODO: Deprecated. Remove after Fx75. "can-be-enabled-change": { type: "canBeEnabledChange", canBeEnabled: Arg(0, "boolean"), @@ -251,6 +253,40 @@ const accessibilitySpec = generateActorSpec({ simulator: RetVal("nullable:simulator"), }, }, + // TODO: Deprecated. Remove after Fx75. + enable: { + request: {}, + response: {}, + }, + // TODO: Deprecated. Remove after Fx75. + disable: { + request: {}, + response: {}, + }, + }, +}); + +const parentAccessibilitySpec = generateActorSpec({ + typeName: "parentaccessibility", + + events: { + "can-be-disabled-change": { + type: "canBeDisabledChange", + canBeDisabled: Arg(0, "boolean"), + }, + "can-be-enabled-change": { + type: "canBeEnabledChange", + canBeEnabled: Arg(0, "boolean"), + }, + }, + + methods: { + bootstrap: { + request: {}, + response: { + state: RetVal("json"), + }, + }, enable: { request: {}, response: {}, @@ -265,4 +301,5 @@ const accessibilitySpec = generateActorSpec({ exports.accessibleSpec = accessibleSpec; exports.accessibleWalkerSpec = accessibleWalkerSpec; exports.accessibilitySpec = accessibilitySpec; +exports.parentAccessibilitySpec = parentAccessibilitySpec; exports.simulatorSpec = simulatorSpec; diff --git a/devtools/shared/specs/index.js b/devtools/shared/specs/index.js index dd06e5396181..743d4406fd86 100644 --- a/devtools/shared/specs/index.js +++ b/devtools/shared/specs/index.js @@ -13,7 +13,12 @@ // This array should be sorted by `spec` attribute. const Types = (exports.__TypesForTests = [ { - types: ["accessible", "accessiblewalker", "accessibility"], + types: [ + "accessible", + "accessiblewalker", + "accessibility", + "parentaccessibility", + ], spec: "devtools/shared/specs/accessibility", front: "devtools/shared/fronts/accessibility", },