The mechanical answer: Fill Holes flood-fills the background from the image border inward, marking every background pixel it can reach. When the flood is done, anything not marked is — by definition — unreachable from the border, which means it's enclosed by foreground. Those unmarked pixels are converted to foreground.
This is conceptually simple and computationally fast (one pass over the image). It's also why edge-touching holes stay open: the flood reaches them on its first move, and once they're marked as reachable, the engine has no further interest in them.