The visual viewport is, per spec supposed to exclude layout (i.e. non-overlay) scrollbars. The MobileViewportManager does this correctly when it's asked to compute the visual viewport, here. However, the problem is that it's not asked to recompute the visual viewport after the root scrollframe does a reflow, which may change the present of the scrollbars.
So after the root scrollframe does a reflow, we need to trigger a recomputation of the visual viewport. That's easy enough.
The next thing to keep in mind is that when the visual viewport changes, we need to reflow the fixed-pos items, because they take the visual viewport into account when positioning themselves, here. So if the root scrollframe does a reflow which adds/removes scrollbars, and then we update the visual viewport, we then need to do another reflow to update the fixed-pos items. OR, we can do it all in a single reflow, if we make sure to recompute the visual viewport between the root scrollframe doing it's thing, and the fixed-pos items doing theirs. This latter approach will reduce work, and so is preferable.
Ok, so that's not hard either. The only other complication is that when the visual viewport updates, it marks various things as dirty, here, but doing that is not allowed in the middle of a reflow (presumably because the items being marked dirty may already have been reflowed, and then the dirty flags get cleared at the end of the reflow, leading to potentially dirty things left behind).
Oh and the one last complication is that if font inflation is enabled, we also call this function to reflow descendant subframes. Although the placement of this call is kind of unorthodox (i.e. it happens on any call to set the visual viewport, even if there was no change), it still fits within the overall solution model we have, because we can (a) reflow the root scrollframe, (b) update the visual viewport, and then (c) reflow fixed-pos and mark subdocuments dirty, and that can all happen within a single reflow.