MacPWAs: Ensure there is always delayed work

Create an infinite loop of once-per-day PostDelayedTasks. This
fixes the following bugs that happen if a PostDelayedTask has been
posted but no PostDelayedTasks remain:
- Menu items remain hilighted after selection via hotkeys.
- Menu doesn't show up in fullscreen when mouse moves to the top of the
- NSAlerts don't show up.

Because it came up during investigation, and while we're in the
neighborhood, ensure that we are precise about when our NSApplication
is created and what subclass it has.

(cherry picked from commit b73d26f4d67cf2a5325c9f51441d94ae78ecd761)

Bug: 920795
Change-Id: I45772bd8cf65899e27ce22a0787db5be454b81be
Commit-Queue: ccameron <>
Reviewed-by: Robert Sesek <>
Cr-Original-Commit-Position: refs/heads/master@{#626818}
Reviewed-by: ccameron <>
Cr-Commit-Position: refs/branch-heads/3683@{#127}
Cr-Branched-From: e51029943e0a38dd794b73caaf6373d5496ae783-refs/heads/master@{#625896}
diff --git a/chrome/app_shim/ b/chrome/app_shim/
index bc20426..026678d 100644
--- a/chrome/app_shim/
+++ b/chrome/app_shim/
@@ -49,6 +49,14 @@
 }  // namespace
+// The NSApplication for app shims is a vanilla NSApplication, but sub-class it
+// so that we can DCHECK that we know precisely when it is initialized.
+@interface AppShimApplication : NSApplication
+@implementation AppShimApplication
 // A ReplyEventHandler is a helper class to send an Apple Event to a process
 // and call a callback when the reply returns.
@@ -165,6 +173,12 @@
 }  // extern "C"
+void PostRepeatingDelayedTask() {
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, base::BindOnce(&PostRepeatingDelayedTask),
+      base::TimeDelta::FromDays(1));
 int ChromeAppModeStart_v5(const app_mode::ChromeAppModeInfo* info) {
   base::CommandLine::Init(info->argc, info->argv);
@@ -254,11 +268,24 @@
       pid = [[existing_chrome objectAtIndex:0] processIdentifier];
+  // Initialize the NSApplication (and ensure that it was not previously
+  // initialized).
+  [AppShimApplication sharedApplication];
+  CHECK([NSApp isKindOfClass:[AppShimApplication class]]);
   base::MessageLoopForUI main_message_loop;
   AppShimController controller(info);
+  // TODO( This workaround ensures that there is
+  // always delayed work enqueued. If there is ever not enqueued delayed work,
+  // then NSMenus and NSAlerts can start misbehaving (see
+  // for examples). This workaround is not an
+  // appropriate solution to the problem, and should be replaced by a fix in
+  // the relevant message pump code.
+  PostRepeatingDelayedTask();
   // In tests, launching Chrome does nothing, and we won't get a ping response,
   // so just assume the socket exists.
   if (pid == -1 &&