updateCaptions_(forceUpdate = false) {
    if (!this.textContainer_) {
      return;
    }

    const currentTime = this.video_.currentTime;
    if (!this.isTextVisible_ || forceUpdate) {
      // Remove child elements from all regions.
      for (const regionElement of this.regionElements_.values()) {
        shaka.util.Dom.removeAllChildren(regionElement);
      }
      // Remove all top-level elements in the text container.
      shaka.util.Dom.removeAllChildren(this.textContainer_);
      // Clear the element maps.
      this.currentCuesMap_.clear();
      this.regionElements_.clear();
    }
    if (this.isTextVisible_) {
      // Log currently attached cue elements for verification, later.
      const previousCuesMap = new Map();
      if (goog.DEBUG) {
        for (const cue of this.currentCuesMap_.keys()) {
          previousCuesMap.set(cue, this.currentCuesMap_.get(cue));
        }
      }

      // Update the cues.
      this.updateCuesRecursive_(
          this.cues_, this.textContainer_, currentTime, /* parents= */ []);

      if (goog.DEBUG) {
        // Previously, we had an issue (#2076) where cues sometimes were not
        // properly removed from the DOM. It is not clear if this issue still
        // happens, so the previous fix for it has been changed to an assert.
        for (const cue of previousCuesMap.keys()) {
          if (!this.currentCuesMap_.has(cue)) {
            // TODO: If the problem does not appear again, then we should remove
            // this assert (and the previousCuesMap code) in Shaka v4.
            const cueElement = previousCuesMap.get(cue).cueElement;
            goog.asserts.assert(
                !cueElement.parentNode, 'Cue was not properly removed!');
          }
        }
      }
    }
  }
Display the current captions.
@param {boolean=} forceUpdate
@private