Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  Common.sys.mjs   Sprache: unbekannt

 
/* 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/. */

import { Assert } from "resource://testing-common/Assert.sys.mjs";

const MAX_TRIM_LENGTH = 100;

export const CommonUtils = {
  /**
   * Constant passed to getAccessible to indicate that it shouldn't fail if
   * there is no accessible.
   */
  DONOTFAIL_IF_NO_ACC: 1,

  /**
   * Constant passed to getAccessible to indicate that it shouldn't fail if it
   * does not support an interface.
   */
  DONOTFAIL_IF_NO_INTERFACE: 2,

  /**
   * nsIAccessibilityService service.
   */
  get accService() {
    if (!this._accService) {
      this._accService = Cc["@mozilla.org/accessibilityService;1"].getService(
        Ci.nsIAccessibilityService
      );
    }

    return this._accService;
  },

  clearAccService() {
    this._accService = null;
    Cu.forceGC();
  },

  /**
   * Adds an observer for an 'a11y-consumers-changed' event.
   */
  addAccConsumersChangedObserver() {
    const deferred = {};
    this._accConsumersChanged = new Promise(resolve => {
      deferred.resolve = resolve;
    });
    const observe = (subject, topic, data) => {
      Services.obs.removeObserver(observe, "a11y-consumers-changed");
      deferred.resolve(JSON.parse(data));
    };
    Services.obs.addObserver(observe, "a11y-consumers-changed");
  },

  /**
   * Returns a promise that resolves when 'a11y-consumers-changed' event is
   * fired.
   *
   * @return {Promise}
   *         event promise evaluating to event's data
   */
  observeAccConsumersChanged() {
    return this._accConsumersChanged;
  },

  /**
   * Adds an observer for an 'a11y-init-or-shutdown' event with a value of "1"
   * which indicates that an accessibility service is initialized in the current
   * process.
   */
  addAccServiceInitializedObserver() {
    const deferred = {};
    this._accServiceInitialized = new Promise((resolve, reject) => {
      deferred.resolve = resolve;
      deferred.reject = reject;
    });
    const observe = (subject, topic, data) => {
      if (data === "1") {
        Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
        deferred.resolve();
      } else {
        deferred.reject("Accessibility service is shutdown unexpectedly.");
      }
    };
    Services.obs.addObserver(observe, "a11y-init-or-shutdown");
  },

  /**
   * Returns a promise that resolves when an accessibility service is
   * initialized in the current process. Otherwise (if the service is shutdown)
   * the promise is rejected.
   */
  observeAccServiceInitialized() {
    return this._accServiceInitialized;
  },

  /**
   * Adds an observer for an 'a11y-init-or-shutdown' event with a value of "0"
   * which indicates that an accessibility service is shutdown in the current
   * process.
   */
  addAccServiceShutdownObserver() {
    const deferred = {};
    this._accServiceShutdown = new Promise((resolve, reject) => {
      deferred.resolve = resolve;
      deferred.reject = reject;
    });
    const observe = (subject, topic, data) => {
      if (data === "0") {
        Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
        deferred.resolve();
      } else {
        deferred.reject("Accessibility service is initialized unexpectedly.");
      }
    };
    Services.obs.addObserver(observe, "a11y-init-or-shutdown");
  },

  /**
   * Returns a promise that resolves when an accessibility service is shutdown
   * in the current process. Otherwise (if the service is initialized) the
   * promise is rejected.
   */
  observeAccServiceShutdown() {
    return this._accServiceShutdown;
  },

  /**
   * Extract DOMNode id from an accessible. If the accessible is in the remote
   * process, DOMNode is not present in parent process. However, if specified by
   * the author, DOMNode id will be attached to an accessible object.
   *
   * @param  {nsIAccessible} accessible  accessible
   * @return {String?}                   DOMNode id if available
   */
  getAccessibleDOMNodeID(accessible) {
    if (accessible instanceof Ci.nsIAccessibleDocument) {
      // If accessible is a document, trying to find its document body id.
      try {
        return accessible.DOMNode.body.id;
      } catch (e) {
        /* This only works if accessible is not a proxy. */
      }
    }
    try {
      return accessible.DOMNode.id;
    } catch (e) {
      /* This will fail if DOMNode is in different process. */
    }
    try {
      // When e10s is enabled, accessible will have an "id" property if its
      // corresponding DOMNode has an id. If accessible is a document, its "id"
      // property corresponds to the "id" of its body element.
      return accessible.id;
    } catch (e) {
      /* This will fail if accessible is not a proxy. */
    }

    return null;
  },

  getObjAddress(obj) {
    const exp = /native\s*@\s*(0x[a-f0-9]+)/g;
    const match = exp.exec(obj.toString());
    if (match) {
      return match[1];
    }

    return obj.toString();
  },

  getNodePrettyName(node) {
    try {
      let tag = "";
      if (node.nodeType == Node.DOCUMENT_NODE) {
        tag = "document";
      } else {
        tag = node.localName;
        if (node.nodeType == Node.ELEMENT_NODE && node.hasAttribute("id")) {
          tag += `@id="${node.getAttribute("id")}"`;
        }
      }

      return `"${tag} node", address: ${this.getObjAddress(node)}`;
    } catch (e) {
      return `" no node info "`;
    }
  },

  /**
   * Convert role to human readable string.
   */
  roleToString(role) {
    return this.accService.getStringRole(role);
  },

  /**
   * Shorten a long string if it exceeds MAX_TRIM_LENGTH.
   *
   * @param aString the string to shorten.
   *
   * @returns the shortened string.
   */
  shortenString(str) {
    if (str.length <= MAX_TRIM_LENGTH) {
      return str;
    }

    // Trim the string if its length is > MAX_TRIM_LENGTH characters.
    const trimOffset = MAX_TRIM_LENGTH / 2;

    return `${str.substring(0, trimOffset - 1)}…${str.substring(
      str.length - trimOffset,
      str.length
    )}`;
  },

  normalizeAccTreeObj(obj) {
    const key = Object.keys(obj)[0];
    const roleName = `ROLE_${key}`;
    if (roleName in Ci.nsIAccessibleRole) {
      return {
        role: Ci.nsIAccessibleRole[roleName],
        children: obj[key],
      };
    }

    return obj;
  },

  stringifyTree(obj) {
    let text = this.roleToString(obj.role) + ": [ ";
    if ("children" in obj) {
      for (let i = 0; i < obj.children.length; i++) {
        const c = this.normalizeAccTreeObj(obj.children[i]);
        text += this.stringifyTree(c);
        if (i < obj.children.length - 1) {
          text += ", ";
        }
      }
    }

    return `${text}] `;
  },

  /**
   * Return pretty name for identifier, it may be ID, DOM node or accessible.
   */
  prettyName(identifier) {
    if (identifier instanceof Array) {
      let msg = "";
      for (let idx = 0; idx < identifier.length; idx++) {
        if (msg != "") {
          msg += ", ";
        }

        msg += this.prettyName(identifier[idx]);
      }
      return msg;
    }

    if (identifier instanceof Ci.nsIAccessible) {
      const acc = this.getAccessible(identifier);
      const domID = this.getAccessibleDOMNodeID(acc);
      let msg = "[";
      try {
        if (Services.appinfo.browserTabsRemoteAutostart) {
          if (domID) {
            msg += `DOM node id: ${domID}, `;
          }
        } else {
          msg += `${this.getNodePrettyName(acc.DOMNode)}, `;
        }
        msg += `role: ${this.roleToString(acc.role)}`;
        if (acc.name) {
          msg += `, name: "${this.shortenString(acc.name)}"`;
        }
      } catch (e) {
        msg += "defunct";
      }

      if (acc) {
        msg += `, address: ${this.getObjAddress(acc)}`;
      }
      msg += "]";

      return msg;
    }

    if (Node.isInstance(identifier)) {
      return `[ ${this.getNodePrettyName(identifier)} ]`;
    }

    if (identifier && typeof identifier === "object") {
      const treeObj = this.normalizeAccTreeObj(identifier);
      if ("role" in treeObj) {
        return `{ ${this.stringifyTree(treeObj)} }`;
      }

      return JSON.stringify(identifier);
    }

    return ` "${identifier}" `;
  },

  /**
   * Return accessible for the given identifier (may be ID attribute or DOM
   * element or accessible object) or null.
   *
   * @param accOrElmOrID
   *        identifier to get an accessible implementing the given interfaces
   * @param aInterfaces
   *        [optional] the interface or an array interfaces to query it/them
   *        from obtained accessible
   * @param elmObj
   *        [optional] object to store DOM element which accessible is obtained
   *        for
   * @param doNotFailIf
   *        [optional] no error for special cases (see DONOTFAIL_IF_NO_ACC,
   *        DONOTFAIL_IF_NO_INTERFACE)
   * @param doc
   *        [optional] document for when accOrElmOrID is an ID.
   */
  getAccessible(accOrElmOrID, interfaces, elmObj, doNotFailIf, doc) {
    if (!accOrElmOrID) {
      return null;
    }

    let elm = null;
    if (accOrElmOrID instanceof Ci.nsIAccessible) {
      try {
        elm = accOrElmOrID.DOMNode;
      } catch (e) {}
    } else if (Node.isInstance(accOrElmOrID)) {
      elm = accOrElmOrID;
    } else {
      elm = doc.getElementById(accOrElmOrID);
      if (!elm) {
        Assert.ok(false, `Can't get DOM element for ${accOrElmOrID}`);
        return null;
      }
    }

    if (elmObj && typeof elmObj == "object") {
      elmObj.value = elm;
    }

    let acc = accOrElmOrID instanceof Ci.nsIAccessible ? accOrElmOrID : null;
    if (!acc) {
      try {
        acc = this.accService.getAccessibleFor(elm);
      } catch (e) {}

      if (!acc) {
        if (!(doNotFailIf & this.DONOTFAIL_IF_NO_ACC)) {
          Assert.ok(
            false,
            `Can't get accessible for ${this.prettyName(accOrElmOrID)}`
          );
        }

        return null;
      }
    }

    if (!interfaces) {
      return acc;
    }

    if (!(interfaces instanceof Array)) {
      interfaces = [interfaces];
    }

    for (let index = 0; index < interfaces.length; index++) {
      if (acc instanceof interfaces[index]) {
        continue;
      }

      try {
        acc.QueryInterface(interfaces[index]);
      } catch (e) {
        if (!(doNotFailIf & this.DONOTFAIL_IF_NO_INTERFACE)) {
          Assert.ok(
            false,
            `Can't query ${interfaces[index]} for ${accOrElmOrID}`
          );
        }

        return null;
      }
    }

    return acc;
  },

  /**
   * Return the DOM node by identifier (may be accessible, DOM node or ID).
   */
  getNode(accOrNodeOrID, doc) {
    if (!accOrNodeOrID) {
      return null;
    }

    if (Node.isInstance(accOrNodeOrID)) {
      return accOrNodeOrID;
    }

    if (accOrNodeOrID instanceof Ci.nsIAccessible) {
      return accOrNodeOrID.DOMNode;
    }

    const node = doc.getElementById(accOrNodeOrID);
    if (!node) {
      Assert.ok(false, `Can't get DOM element for ${accOrNodeOrID}`);
      return null;
    }

    return node;
  },

  /**
   * Return root accessible.
   *
   * @param  {DOMNode} doc
   *         Chrome document.
   *
   * @return {nsIAccessible}
   *         Accessible object for chrome window.
   */
  getRootAccessible(doc) {
    const acc = this.getAccessible(doc);
    return acc ? acc.rootDocument.QueryInterface(Ci.nsIAccessible) : null;
  },

  /**
   * Analogy of SimpleTest.is function used to compare objects.
   */
  isObject(obj, expectedObj, msg) {
    if (obj == expectedObj) {
      Assert.ok(true, msg);
      return;
    }

    Assert.ok(
      false,
      `${msg} - got "${this.prettyName(obj)}", expected "${this.prettyName(
        expectedObj
      )}"`
    );
  },
};

[ Dauer der Verarbeitung: 0.34 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge