tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

accessibility-view.js (10398B)


      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 "use strict";
      5 
      6 /* global EVENTS */
      7 
      8 const nodeConstants = require("resource://devtools/shared/dom-node-constants.js");
      9 
     10 // React & Redux
     11 const {
     12  createFactory,
     13  createElement,
     14 } = require("resource://devtools/client/shared/vendor/react.mjs");
     15 const ReactDOM = require("resource://devtools/client/shared/vendor/react-dom.mjs");
     16 const {
     17  Provider,
     18 } = require("resource://devtools/client/shared/vendor/react-redux.js");
     19 
     20 // Accessibility Panel
     21 const MainFrame = createFactory(
     22  require("resource://devtools/client/accessibility/components/MainFrame.js")
     23 );
     24 
     25 // Store
     26 const createStore = require("resource://devtools/client/shared/redux/create-store.js");
     27 
     28 // Reducers
     29 const {
     30  reducers,
     31 } = require("resource://devtools/client/accessibility/reducers/index.js");
     32 const thunkOptions = { options: {} };
     33 const store = createStore(reducers, {
     34  // Thunk options will be updated, when we [re]initialize the accessibility
     35  // view.
     36  thunkOptions,
     37 });
     38 
     39 // Actions
     40 const {
     41  reset,
     42 } = require("resource://devtools/client/accessibility/actions/ui.js");
     43 const {
     44  select,
     45  highlight,
     46 } = require("resource://devtools/client/accessibility/actions/accessibles.js");
     47 
     48 /**
     49 * This object represents view of the Accessibility panel and is responsible
     50 * for rendering the content. It renders the top level ReactJS
     51 * component: the MainFrame.
     52 */
     53 class AccessibilityView {
     54  constructor(localStore) {
     55    addEventListener(
     56      "devtools/chrome/message",
     57      this.onMessage.bind(this),
     58      true
     59    );
     60    this.store = localStore;
     61  }
     62  /**
     63   * Initialize accessibility view, create its top level component and set the
     64   * data store.
     65   *
     66   * @param {object}
     67   *        Object that contains the following properties:
     68   * - supports                               {JSON}
     69   *                                          a collection of flags indicating
     70   *                                          which accessibility panel features
     71   *                                          are supported by the current
     72   *                                          serverside version.
     73   * - fluentBundles                          {Array}
     74   *                                          array of FluentBundles elements
     75   *                                          for localization
     76   * - toolbox                                {Object}
     77   *                                          devtools toolbox.
     78   * - getAccessibilityTreeRoot               {Function}
     79   *                                          Returns the topmost accessibiliity
     80   *                                          walker that is used as the root of
     81   *                                          the accessibility tree.
     82   * - startListeningForAccessibilityEvents   {Function}
     83   *                                          Add listeners for specific
     84   *                                          accessibility events.
     85   * - stopListeningForAccessibilityEvents    {Function}
     86   *                                          Remove listeners for specific
     87   *                                          accessibility events.
     88   * - audit                                  {Function}
     89   *                                          Audit function that will start
     90   *                                          accessibility audit for given types
     91   *                                          of accessibility issues.
     92   * - simulate                               {null|Function}
     93   *                                          Apply simulation of a given type
     94   *                                          (by setting color matrices in
     95   *                                          docShell).
     96   * - toggleDisplayTabbingOrder              {Function}
     97   *                                          Toggle the highlight of focusable
     98   *                                          elements along with their tabbing
     99   *                                          index.
    100   * - enableAccessibility                    {Function}
    101   *                                          Enable accessibility services.
    102   * - resetAccessiblity                      {Function}
    103   *                                          Reset the state of the
    104   *                                          accessibility services.
    105   * - startListeningForLifecycleEvents       {Function}
    106   *                                          Add listeners for accessibility
    107   *                                          service lifecycle events.
    108   * - stopListeningForLifecycleEvents        {Function}
    109   *                                          Remove listeners for accessibility
    110   *                                          service lifecycle events.
    111   * - startListeningForParentLifecycleEvents {Function}
    112   *                                          Add listeners for parent process
    113   *                                          accessibility service lifecycle
    114   *                                          events.
    115   * - stopListeningForParentLifecycleEvents  {Function}
    116   *                                          Remove listeners for parent
    117   *                                          process accessibility service
    118   *                                          lifecycle events.
    119   * - highlightAccessible                    {Function}
    120   *                                          Highlight accessible object.
    121   * - unhighlightAccessible                  {Function}
    122   *                                          Unhighlight accessible object.
    123   */
    124  async initialize({
    125    supports,
    126    fluentBundles,
    127    toolbox,
    128    getAccessibilityTreeRoot,
    129    startListeningForAccessibilityEvents,
    130    stopListeningForAccessibilityEvents,
    131    audit,
    132    simulate,
    133    toggleDisplayTabbingOrder,
    134    enableAccessibility,
    135    resetAccessiblity,
    136    startListeningForLifecycleEvents,
    137    stopListeningForLifecycleEvents,
    138    startListeningForParentLifecycleEvents,
    139    stopListeningForParentLifecycleEvents,
    140    highlightAccessible,
    141    unhighlightAccessible,
    142  }) {
    143    // Make sure state is reset every time accessibility panel is initialized.
    144    await this.store.dispatch(reset(resetAccessiblity, supports));
    145    const container = document.getElementById("content");
    146    const mainFrame = MainFrame({
    147      fluentBundles,
    148      toolbox,
    149      getAccessibilityTreeRoot,
    150      startListeningForAccessibilityEvents,
    151      stopListeningForAccessibilityEvents,
    152      audit,
    153      simulate,
    154      enableAccessibility,
    155      resetAccessiblity,
    156      startListeningForLifecycleEvents,
    157      stopListeningForLifecycleEvents,
    158      startListeningForParentLifecycleEvents,
    159      stopListeningForParentLifecycleEvents,
    160      highlightAccessible,
    161      unhighlightAccessible,
    162    });
    163    thunkOptions.options.toggleDisplayTabbingOrder = toggleDisplayTabbingOrder;
    164    // Render top level component
    165    const provider = createElement(Provider, { store: this.store }, mainFrame);
    166 
    167    // If the AccessibilityService can not be turned on, directly emit the INITIALIZED event
    168    // so the panel can be rendered (with the Description element).
    169    if (!this.store.getState().ui.canBeEnabled) {
    170      window.emit(EVENTS.INITIALIZED);
    171    } else {
    172      window.once(EVENTS.PROPERTIES_UPDATED).then(() => {
    173        window.emit(EVENTS.INITIALIZED);
    174      });
    175    }
    176    this.mainFrame = ReactDOM.render(provider, container);
    177  }
    178 
    179  destroy() {
    180    const container = document.getElementById("content");
    181    ReactDOM.unmountComponentAtNode(container);
    182  }
    183 
    184  async selectAccessible(accessible) {
    185    await this.store.dispatch(select(accessible));
    186    window.emit(EVENTS.NEW_ACCESSIBLE_FRONT_INSPECTED);
    187  }
    188 
    189  async highlightAccessible(accessible) {
    190    await this.store.dispatch(highlight(accessible));
    191    window.emit(EVENTS.NEW_ACCESSIBLE_FRONT_HIGHLIGHTED);
    192  }
    193 
    194  async selectNodeAccessible(node) {
    195    if (!node) {
    196      return;
    197    }
    198 
    199    const accessibilityFront = await node.targetFront.getFront("accessibility");
    200    const accessibleWalkerFront = await accessibilityFront.getWalker();
    201    let accessible = await accessibleWalkerFront.getAccessibleFor(node);
    202    if (accessible) {
    203      await accessible.hydrate();
    204    }
    205 
    206    // If node does not have an accessible object, try to find node's child text node and
    207    // try to retrieve an accessible object for that child instead. This is the best
    208    // effort approach until there's accessibility API to retrieve accessible object at
    209    // point.
    210    if (!accessible || accessible.indexInParent < 0) {
    211      const { nodes: children } = await node.walkerFront.children(node);
    212      for (const child of children) {
    213        if (child.nodeType === nodeConstants.TEXT_NODE) {
    214          accessible = await accessibleWalkerFront.getAccessibleFor(child);
    215          // indexInParent property is only available with additional request
    216          // for data (hydration) about the accessible object.
    217          if (accessible) {
    218            await accessible.hydrate();
    219            if (accessible.indexInParent >= 0) {
    220              break;
    221            }
    222          }
    223        }
    224      }
    225    }
    226 
    227    // Attempt to find closest accessible ancestor for a given node.
    228    if (!accessible || accessible.indexInParent < 0) {
    229      let parentNode = node.parentNode();
    230      while (parentNode) {
    231        accessible = await accessibleWalkerFront.getAccessibleFor(parentNode);
    232        if (accessible) {
    233          await accessible.hydrate();
    234          if (accessible.indexInParent >= 0) {
    235            break;
    236          }
    237        }
    238 
    239        parentNode = parentNode.parentNode();
    240      }
    241    }
    242 
    243    // Do not set the selected state if there is no corresponding accessible.
    244    if (!accessible) {
    245      console.warn(
    246        `No accessible object found for a node or a node in its ancestry: ${node.actorID}`
    247      );
    248      return;
    249    }
    250 
    251    await this.store.dispatch(select(accessible));
    252    window.emit(EVENTS.NEW_ACCESSIBLE_FRONT_INSPECTED);
    253  }
    254 
    255  /**
    256   * Process message from accessibility panel.
    257   *
    258   * @param {object} event  message type and data.
    259   */
    260  onMessage(event) {
    261    const data = event.data;
    262    const method = data.type;
    263 
    264    if (typeof this[method] === "function") {
    265      this[method](...data.args);
    266    }
    267  }
    268 }
    269 
    270 window.view = new AccessibilityView(store);