async processPatchManifest_(mpd) {
    const TXml = shaka.util.TXml;

    const mpdId = mpd.attributes['mpdId'];
    const originalPublishTime = TXml.parseAttr(mpd, 'originalPublishTime',
        TXml.parseDate);
    if (!mpdId || mpdId !== this.manifestPatchContext_.mpdId ||
        originalPublishTime !== this.manifestPatchContext_.publishTime) {
      // Clean patch location nodes, so it will force full MPD update.
      this.patchLocationNodes_ = [];
      throw new shaka.util.Error(
          shaka.util.Error.Severity.RECOVERABLE,
          shaka.util.Error.Category.MANIFEST,
          shaka.util.Error.Code.DASH_INVALID_PATCH);
    }

    /** @type {!Array<shaka.extern.Period>} */
    const newPeriods = [];
    /** @type {!Array<shaka.extern.xml.Node>} */
    const periodAdditions = [];
    /** @type {!Set<string>} */
    const modifiedTimelines = new Set();

    for (const patchNode of TXml.getChildNodes(mpd)) {
      let handled = true;
      const paths = TXml.parseXpath(patchNode.attributes['sel'] || '');
      const node = paths[paths.length - 1];
      const content = TXml.getContents(patchNode) || '';

      if (node.name === 'MPD') {
        if (node.attribute === 'mediaPresentationDuration') {
          const content = TXml.getContents(patchNode) || '';
          this.parsePatchMediaPresentationDurationChange_(content);
        } else if (node.attribute === 'type') {
          this.parsePatchMpdTypeChange_(content);
        } else if (node.attribute === 'publishTime') {
          this.manifestPatchContext_.publishTime = TXml.parseDate(content) || 0;
        } else if (node.attribute === null && patchNode.tagName === 'add') {
          periodAdditions.push(patchNode);
        } else {
          handled = false;
        }
      } else if (node.name === 'PatchLocation') {
        this.updatePatchLocationNodes_(patchNode);
      } else if (node.name === 'Period') {
        if (patchNode.tagName === 'add') {
          periodAdditions.push(patchNode);
        } else if (patchNode.tagName === 'remove' && node.id) {
          this.removePatchPeriod_(node.id);
        }
      } else if (node.name === 'SegmentTemplate') {
        const timelines = this.modifySegmentTemplate_(patchNode);
        for (const timeline of timelines) {
          modifiedTimelines.add(timeline);
        }
      } else if (node.name === 'SegmentTimeline' || node.name === 'S') {
        const timelines = this.modifyTimepoints_(patchNode);
        for (const timeline of timelines) {
          modifiedTimelines.add(timeline);
        }
      } else {
        handled = false;
      }
      if (!handled) {
        shaka.log.warning('Unhandled ' + patchNode.tagName + ' operation',
            patchNode.attributes['sel']);
      }
    }

    for (const timeline of modifiedTimelines) {
      this.parsePatchSegment_(timeline);
    }

    // Add new periods after extending timelines, as new periods
    // remove context cache of previous periods.
    for (const periodAddition of periodAdditions) {
      newPeriods.push(...this.parsePatchPeriod_(periodAddition));
    }

    if (newPeriods.length) {
      this.manifest_.periodCount += newPeriods.length;
      this.manifest_.gapCount = this.gapCount_;
      await this.postPeriodProcessing_(newPeriods, /* isPatchUpdate= */ true);
    }
    if (this.manifestPatchContext_.type == 'static') {
      const duration = this.manifestPatchContext_.mediaPresentationDuration;
      this.manifest_.presentationTimeline.setDuration(duration || Infinity);
    }
  }
Takes a formatted Patch MPD and converts it into a manifest.

@param {!shaka.extern.xml.Node} mpd
@return {!Promise}
@private