static generateCueFromElement_(element, rootCue, styles) {
    const TXml = shaka.util.TXml;
    const nestedCue = rootCue.clone();
    // We don't want propagate some properties.
    nestedCue.nestedCues = [];
    nestedCue.payload = '';
    nestedCue.rubyTag = '';
    // We don't want propagate some position settings
    nestedCue.line = null;
    nestedCue.region = new shaka.text.CueRegion();
    nestedCue.position = null;
    nestedCue.size = 0;
    nestedCue.textAlign = shaka.text.Cue.textAlign.CENTER;

    if (TXml.isNode(element)) {
      const bold = shaka.text.Cue.fontWeight.BOLD;
      const italic = shaka.text.Cue.fontStyle.ITALIC;
      const underline = shaka.text.Cue.textDecoration.UNDERLINE;
      const tags = element.tagName.split(/(?=[ .])+/g);
      for (const tag of tags) {
        let styleTag = tag;
        // White blanks at start indicate that the style is a voice
        if (styleTag.startsWith('.voice-')) {
          const voice = styleTag.split('-').pop();
          styleTag = `v[voice="${voice}"]`;
          // The specification allows to have quotes and not, so we check to
          // see which one is being used.
          if (!styles.has(styleTag)) {
            styleTag = `v[voice=${voice}]`;
          }
        }
        if (styles.has(styleTag)) {
          shaka.text.Cue.mergeStyle_(nestedCue, styles.get(styleTag));
        }
        switch (tag) {
          case 'br': {
            const lineBreakCue = shaka.text.Cue.lineBreak(
                nestedCue.startTime, nestedCue.endTime);
            rootCue.nestedCues.push(lineBreakCue);
            return;
          }
          case 'b':
            nestedCue.fontWeight = bold;
            break;
          case 'i':
            nestedCue.fontStyle = italic;
            break;
          case 'u':
            nestedCue.textDecoration.push(underline);
            break;
          case 'font': {
            const color = element.attributes['color'];
            if (color) {
              nestedCue.color = color;
            }
            break;
          }
          case 'div': {
            const time = element.attributes['time'];
            if (!time) {
              break;
            }
            const cueTime = shaka.util.TextParser.parseTime(time);
            if (cueTime) {
              nestedCue.startTime = cueTime;
            }
            break;
          }
          case 'ruby':
          case 'rp':
          case 'rt':
            nestedCue.rubyTag = tag;
            break;
          default:
            break;
        }
      }
    }

    const isTextNode = (item) => TXml.isText(item);
    const childNodes = element.children;
    if (isTextNode(element) ||
        (childNodes.length == 1 && isTextNode(childNodes[0]))) {
      // Trailing line breaks may lost when convert cue to HTML tag
      // Need to insert line break cue to preserve line breaks
      const textArr = TXml.getTextContents(element).split('\n');
      let isFirst = true;
      for (const text of textArr) {
        if (!isFirst) {
          const lineBreakCue = shaka.text.Cue.lineBreak(
              nestedCue.startTime, nestedCue.endTime);
          rootCue.nestedCues.push(lineBreakCue);
        }
        if (text.length > 0) {
          const textCue = nestedCue.clone();
          textCue.payload = shaka.util.StringUtils.htmlUnescape(text);
          rootCue.nestedCues.push(textCue);
        }
        isFirst = false;
      }
    } else {
      rootCue.nestedCues.push(nestedCue);
      for (const childNode of childNodes) {
        shaka.text.Cue.generateCueFromElement_(childNode, nestedCue, styles);
      }
    }
  }
@param {!shaka.extern.xml.Node} element
@param {!shaka.text.Cue} rootCue
@param {!Map<string, !shaka.text.Cue>} styles
@private