/* 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 {
  accessibilitySpec,
} = require("resource://devtools/shared/specs/accessibility.js");

loader.lazyRequireGetter(
  this,
  "AccessibleWalkerActor",
  "resource://devtools/server/actors/accessibility/walker.js",
  true
);
loader.lazyRequireGetter(
  this,
  "SimulatorActor",
  "resource://devtools/server/actors/accessibility/simulator.js",
  true
);

/**
 * The AccessibilityActor is a top level container actor that initializes
 * accessible walker and is the top-most point of interaction for accessibility
 * tools UI for a top level content process.
 */
class AccessibilityActor extends Actor {
  constructor(conn, targetActor) {
    super(conn, accessibilitySpec);
    // 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;
  }

  getTraits() {
    // The traits are used to know if accessibility actors support particular
    // API on the server side.
    return {
      // @backward-compat { version 84 } Fixed on the server by Bug 1654956.
      tabbingOrder: true,
    };
  }

  bootstrap() {
    return {
      enabled: this.enabled,
    };
  }

  get enabled() {
    return Services.appinfo.accessibilityEnabled;
  }

  /**
   * Observe Accessibility service init and shutdown events. It relays these
   * events to AccessibilityFront if the event is fired for the a11y service
   * that lives in the same process.
   *
   * @param  {null} subject
   *         Not used.
   * @param  {string} topic
   *         Name of the a11y service event: "a11y-init-or-shutdown".
   * @param  {string} data
   *         "0" corresponds to shutdown and "1" to init.
   */
  observe(subject, topic, data) {
    const enabled = data === "1";
    if (enabled && this.enabled) {
      this.emit("init");
    } else if (!enabled && !this.enabled) {
      if (this.walker) {
        this.walker.reset();
      }

      this.emit("shutdown");
    }
  }

  /**
   * Get or create AccessibilityWalker actor, similar to WalkerActor.
   *
   * @return {object}
   *         AccessibleWalkerActor for the current tab.
   */
  getWalker() {
    if (!this.walker) {
      this.walker = new AccessibleWalkerActor(this.conn, this.targetActor);
      this.manage(this.walker);
    }
    return this.walker;
  }

  /**
   * 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
   * <feColorMatrix> since it is accelerated and scrolling with filter applied
   * needs to be smooth (Bug1431466).
   *
   * @return {object | null}
   *         SimulatorActor for the current tab.
   */
  getSimulator() {
    if (!this.simulator) {
      this.simulator = new SimulatorActor(this.conn, this.targetActor);
      this.manage(this.simulator);
    }

    return this.simulator;
  }

  /**
   * Destroy accessibility actor. This method also shutsdown accessibility
   * service if possible.
   */
  async destroy() {
    super.destroy();
    Services.obs.removeObserver(this, "a11y-init-or-shutdown");
    this.walker = null;
    this.targetActor = null;
  }
}

exports.AccessibilityActor = AccessibilityActor;
