refactor: make stage advancing logic more generic (#94349)
In #93801, we'll be adding 4 new stages. `StagedRenderingController`
already contains a lot of duplication around advancing stages, and i
want to avoid adding to that, so this PR does some cleanup.
Previously, for a stage, we had
- `resolve<name>Stage` - triggers listener callbacks and resolves the
promise for the stage
- `<name>StageListeners` - added via `onStage`, triggered when advancing
to the stage but before resolving the promise
- `<name>StagePromise` - resolved when we enter the stage (or rejected
on abort/abandon)
- sometimes, `<name>EndTime`
these behaviors are all now packaged up into `StageTrigger`, which can
be "fired" (when advancing to a stage) or "cancelled" (when
aborting/abandoning) and tracks the time.
In addition, we now have `RENDER_STAGE_ADVANCE_ORDER`, an array that
defines the order in which advanceable stages (i.e. excluding
Before/Abandoned) should happen. We can loop through this to advance
stages without relying on a huge switch statement with fallthroughs.
Re-working the logic into this shape will be handy in the future, when
we want each route param to have its own stage, and the set of stages is
no longer statically known.