diff --git message_loop.cc message_loop.cc
index 2212941..c2895af 100644
--- message_loop.cc
+++ message_loop.cc
@@ -96,12 +96,6 @@ MessageLoop::~MessageLoop() {
   // may be current.
   DCHECK((pump_ && current() == this) || (!pump_ && current() != this));
 
-  // iOS just attaches to the loop, it doesn't Run it.
-  // TODO(stuartmorgan): Consider wiring up a Detach().
-#if !defined(OS_IOS)
-  DCHECK(!run_loop_);
-#endif
-
 #if defined(OS_WIN)
   if (in_high_res_mode_)
     Time::ActivateHighResolutionTimer(false);
@@ -319,6 +313,9 @@ MessageLoop::MessageLoop(Type type, MessagePumpFactoryCallback pump_factory)
       in_high_res_mode_(false),
 #endif
       nestable_tasks_allowed_(true),
+#if defined(OS_WIN)
+      os_modal_loop_(false),
+#endif  // OS_WIN
       pump_factory_(pump_factory),
       run_loop_(NULL),
       incoming_task_queue_(new internal::IncomingTaskQueue(this)),
diff --git message_loop.h message_loop.h
index 91a7b1d..de864d4 100644
--- message_loop.h
+++ message_loop.h
@@ -303,6 +303,16 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
   void AddTaskObserver(TaskObserver* task_observer);
   void RemoveTaskObserver(TaskObserver* task_observer);
 
+#if defined(OS_WIN)
+  void set_os_modal_loop(bool os_modal_loop) {
+    os_modal_loop_ = os_modal_loop;
+  }
+
+  bool os_modal_loop() const {
+    return os_modal_loop_;
+  }
+#endif  // OS_WIN
+
   // Can only be called from the thread that owns the MessageLoop.
   bool is_running() const;
 
@@ -440,6 +450,12 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
   // insider a (accidentally induced?) nested message pump.
   bool nestable_tasks_allowed_;
 
+#if defined(OS_WIN)
+  // Should be set to true before calling Windows APIs like TrackPopupMenu, etc.
+  // which enter a modal message loop.
+  bool os_modal_loop_;
+#endif
+
   // pump_factory_.Run() is called to create a message pump for this loop
   // if type_ is TYPE_CUSTOM and pump_ is null.
   MessagePumpFactoryCallback pump_factory_;
diff --git message_pump_win.cc message_pump_win.cc
index 30638df..a50d626 100644
--- message_pump_win.cc
+++ message_pump_win.cc
@@ -366,20 +366,28 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
 }
 
 bool MessagePumpForUI::ProcessPumpReplacementMessage() {
-  // When we encounter a kMsgHaveWork message, this method is called to peek and
-  // process a replacement message. The goal is to make the kMsgHaveWork as non-
-  // intrusive as possible, even though a continuous stream of such messages are
-  // posted. This method carefully peeks a message while there is no chance for
-  // a kMsgHaveWork to be pending, then resets the |have_work_| flag (allowing a
-  // replacement kMsgHaveWork to possibly be posted), and finally dispatches
-  // that peeked replacement. Note that the re-post of kMsgHaveWork may be
-  // asynchronous to this thread!!
-
+  // When we encounter a kMsgHaveWork message, this method is called to peek
+  // and process a replacement message, such as a WM_PAINT or WM_TIMER.  The
+  // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
+  // a continuous stream of such messages are posted.  This method carefully
+  // peeks a message while there is no chance for a kMsgHaveWork to be pending,
+  // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
+  // possibly be posted), and finally dispatches that peeked replacement.  Note
+  // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
+
+  bool have_message = false;
   MSG msg;
-  const bool have_message =
-      PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) != FALSE;
+  // We should not process all window messages if we are in the context of an
+  // OS modal loop, i.e. in the context of a windows API call like MessageBox.
+  // This is to ensure that these messages are peeked out by the OS modal loop.
+  if (MessageLoop::current()->os_modal_loop()) {
+    // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above.
+    have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
+                   PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
+  } else {
+    have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE;
+  }
 
-  // Expect no message or a message different than kMsgHaveWork.
   DCHECK(!have_message || kMsgHaveWork != msg.message ||
          msg.hwnd != message_window_.hwnd());