base: Defer quitting run loop after modal window is closed

This patch modifies MessagePumpNSApplication to support calling Quit() while a
modal window is being displayed. Previously we would stop the wrong run loop in
this scenario:

  1. Start a Chrome run loop with RunLoop::Run().
  2. Start a modal session with [NSApplication runModalForWindow].
  3. Call RunLoop::Quit().

Because the dialog is still active in step #3, calling [NSApplication stop]
applies to the internal modal run loop instead of the one started in step #1.

The fix is to only quit the run loop when the dialog goes away. We do this by
observing when we re-enter Chrome's run loop after the nested modal run loop
has exited.

Note that unlike MessagePumpCFRunLoop we can't call [NSApplication stop] as
soon as we detect the nested run loop exiting. This is because when the exit
observer is notified, [NSApplication stop] still signals the exiting internal
run loop instead the Chrome one.

For posterity, NSApplication also has another quirk regarding modal windows:
[NSApplication stopModal] does not take effect immediately, but instead we may
execute at least one Chrome task in the nested run loop before
runModalForWindow returns.

Bug: 891670
Change-Id: I8e05fdafd32c70a4ebf073472c998304d458ea48
Reviewed-on: https://chromium-review.googlesource.com/c/1384289
Commit-Queue: Sami Kyöstilä <skyostil@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#618227}
4 files changed