+ * @return SlotDiffRenderer[] Diff renderers for each slot, keyed by role name.
+ * Includes slots only present in one of the revisions.
+ */
+ protected function getSlotDiffRenderers() {
+ if ( $this->isSlotDiffRenderer ) {
+ throw new LogicException( __METHOD__ . ' called in slot diff renderer mode' );
+ }
+
+ if ( $this->slotDiffRenderers === null ) {
+ if ( !$this->loadRevisionData() ) {
+ return [];
+ }
+
+ $slotContents = $this->getSlotContents();
+ $this->slotDiffRenderers = array_map( function ( $contents ) {
+ /** @var $content Content */
+ $content = $contents['new'] ?: $contents['old'];
+ return $content->getContentHandler()->getSlotDiffRenderer( $this->getContext() );
+ }, $slotContents );
+ }
+ return $this->slotDiffRenderers;
+ }
+
+ /**
+ * Mark this DifferenceEngine as a slot renderer (as opposed to a page renderer).
+ * This is used in legacy mode when the DifferenceEngine is wrapped in a
+ * DifferenceEngineSlotDiffRenderer.
+ * @internal For use by DifferenceEngineSlotDiffRenderer only.
+ */
+ public function markAsSlotDiffRenderer() {
+ $this->isSlotDiffRenderer = true;
+ }
+
+ /**
+ * Get the old and new content objects for all slots.
+ * This method does not do any permission checks.
+ * @return array [ role => [ 'old' => SlotRecord|null, 'new' => SlotRecord|null ], ... ]
+ */
+ protected function getSlotContents() {
+ if ( $this->isContentOverridden ) {
+ return [
+ 'main' => [
+ 'old' => $this->mOldContent,
+ 'new' => $this->mNewContent,
+ ]
+ ];
+ } elseif ( !$this->loadRevisionData() ) {
+ return [];
+ }
+
+ $newSlots = $this->mNewRev->getRevisionRecord()->getSlots()->getSlots();
+ if ( $this->mOldRev ) {
+ $oldSlots = $this->mOldRev->getRevisionRecord()->getSlots()->getSlots();
+ } else {
+ $oldSlots = [];
+ }
+ // The order here will determine the visual order of the diff. The current logic is
+ // slots of the new revision first in natural order, then deleted ones. This is ad hoc
+ // and should not be relied on - in the future we may want the ordering to depend
+ // on the page type.
+ $roles = array_merge( array_keys( $newSlots ), array_keys( $oldSlots ) );
+
+ $slots = [];
+ foreach ( $roles as $role ) {
+ $slots[$role] = [
+ 'old' => isset( $oldSlots[$role] ) ? $oldSlots[$role]->getContent() : null,
+ 'new' => isset( $newSlots[$role] ) ? $newSlots[$role]->getContent() : null,
+ ];
+ }
+ // move main slot to front
+ if ( isset( $slots['main'] ) ) {
+ $slots = [ 'main' => $slots['main'] ] + $slots;
+ }
+ return $slots;
+ }
+
+ public function getTitle() {
+ // T202454 avoid errors when there is no title
+ return parent::getTitle() ?: Title::makeTitle( NS_SPECIAL, 'BadTitle/DifferenceEngine' );
+ }
+
+ /**
+ * Set reduced line numbers mode.
+ * When set, line X is not displayed when X is 1, for example to increase readability and
+ * conserve space with many small diffs.