chromium/chrome/browser/resources/chromeos/accessibility/chromevox/background/math_handler.js

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Handles math output and exploration.
 */
import {AutomationPredicate} from '/common/automation_predicate.js';
import {CursorRange} from '/common/cursors/range.js';

import {Msgs} from '../common/msgs.js';
import {QueueMode} from '../common/tts_types.js';

import {ChromeVox} from './chromevox.js';

/**
 * Handles specialized code to navigate, announce, and interact with math
 * content (encoded in MathML).
 */
export class MathHandler {
  /**
   * @param {!chrome.automation.AutomationNode} node
   */
  constructor(node) {
    /** @private {!chrome.automation.AutomationNode} */
    this.node_ = node;
  }

  /**
   * Speaks the current node.
   * @return {boolean} Whether any math was spoken.
   */
  speak() {
    let mathml;

    // Math can exist either as explicit innerHtml (handled by the Blink
    // renderer for nodes with role math) or as a data attribute.
    if (this.node_.role === chrome.automation.RoleType.MATH &&
        this.node_.innerHtml) {
      mathml = this.node_.innerHtml;
    } else {
      mathml = this.node_.htmlAttributes['data-mathml'];
    }

    if (!mathml) {
      return false;
    }

    let text;

    try {
      text = SRE.walk(mathml);
    } catch (e) {
      // Swallow exceptions from third party library.
    }

    if (!text) {
      return false;
    }

    ChromeVox.tts.speak(text, QueueMode.FLUSH);
    ChromeVox.tts.speak(Msgs.getMsg('hint_math_keyboard'), QueueMode.QUEUE);
    return true;
  }

  /**
   * Initializes the global instance.
   * @param {CursorRange} range
   * @return {boolean} True if an instance was created.
   */
  static init(range) {
    const node = range.start.node;
    if (node && AutomationPredicate.math(node)) {
      MathHandler.instance = new MathHandler(node);
    } else {
      MathHandler.instance = undefined;
    }
    return Boolean(MathHandler.instance);
  }

  /**
   * Handles key events.
   * @return {boolean} False to prevent further event propagation.
   */
  static onKeyDown(evt) {
    if (!MathHandler.instance) {
      return true;
    }

    if (evt.ctrlKey || evt.altKey || evt.metaKey || evt.shiftKey ||
        evt.stickyMode) {
      return true;
    }

    const instance = MathHandler.instance;
    const output = SRE.move(evt.keyCode);
    if (output) {
      ChromeVox.tts.speak(output, QueueMode.FLUSH);
    }
    return false;
  }
}


/**
 * The global instance.
 * @type {MathHandler|undefined}
 */
MathHandler.instance;