Bug 1375491, make child process to cache ime properties only at animation tick time, r=masayuki
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 29 Jun 2017 14:46:11 +0300
changeset 366512 7eea0e7fbff9b6ccb5b6e9400b1278ec58dbca6b
parent 366511 c1983642406a1ad7768d5d2363e521c11b4f1b2a
child 366513 0b170a89dce40d227f3c7f6bbff9411ffd11c357
push id92004
push useropettay@mozilla.com
push dateThu, 29 Jun 2017 12:00:30 +0000
treeherdermozilla-inbound@7eea0e7fbff9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs1375491
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1375491, make child process to cache ime properties only at animation tick time, r=masayuki
dom/base/Selection.cpp
dom/events/EventStateManager.h
dom/events/IMEContentObserver.cpp
dom/events/IMEContentObserver.h
dom/events/UIEvent.cpp
editor/libeditor/tests/test_bug551704.html
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -3494,17 +3494,17 @@ Selection::PostScrollSelectionIntoViewEv
   NS_ENSURE_STATE(presContext);
   nsRefreshDriver* refreshDriver = presContext->RefreshDriver();
   NS_ENSURE_STATE(refreshDriver);
 
   RefPtr<ScrollSelectionIntoViewEvent> ev =
     new ScrollSelectionIntoViewEvent(this, aRegion, aVertical, aHorizontal,
                                      aFlags);
   mScrollEvent = ev;
-  refreshDriver->AddPendingSelectionScroll(ev);
+  refreshDriver->AddEarlyRunner(ev);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Selection::ScrollIntoView(SelectionRegion aRegion, bool aIsSynchronous,
                           int16_t aVPercent, int16_t aHPercent)
 {
   ErrorResult result;
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -148,16 +148,18 @@ public:
    * observe or stops observing the content.
    */
   void OnStartToObserveContent(IMEContentObserver* aIMEContentObserver);
   void OnStopObservingContent(IMEContentObserver* aIMEContentObserver);
 
   /**
    * TryToFlushPendingNotificationsToIME() suggests flushing pending
    * notifications to IME to IMEContentObserver.
+   * Doesn't do anything in child processes where flushing happens
+   * asynchronously.
    */
   void TryToFlushPendingNotificationsToIME();
 
   /**
    * Register accesskey on the given element. When accesskey is activated then
    * the element will be notified via nsIContent::PerformAccesskey() method.
    *
    * @param  aContent  the given element
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -26,16 +26,17 @@
 #include "nsIFrame.h"
 #include "nsINode.h"
 #include "nsIPresShell.h"
 #include "nsISelectionController.h"
 #include "nsISelectionPrivate.h"
 #include "nsISupports.h"
 #include "nsIWidget.h"
 #include "nsPresContext.h"
+#include "nsRefreshDriver.h"
 #include "nsWeakReference.h"
 #include "WritingModes.h"
 
 namespace mozilla {
 
 using namespace widget;
 
 LazyLogModule sIMECOLog("IMEContentObserver");
@@ -1726,34 +1727,27 @@ IMEContentObserver::FlushMergeableNotifi
      "creating IMENotificationSender...", this));
 
   // If contents in selection range is modified, the selection range still
   // has removed node from the tree.  In such case, nsContentIterator won't
   // work well.  Therefore, we shouldn't use AddScriptRunnder() here since
   // it may kick runnable event immediately after DOM tree is changed but
   // the selection range isn't modified yet.
   mQueuedSender = new IMENotificationSender(this);
-  nsIScriptGlobalObject* globalObject = mDocShell ?
-                                        mDocShell->GetScriptGlobalObject() :
-                                        nullptr;
-  if (globalObject) {
-    RefPtr<IMENotificationSender> queuedSender = mQueuedSender;
-    globalObject->Dispatch(nullptr, TaskCategory::Other, queuedSender.forget());
-  } else {
-    NS_DispatchToCurrentThread(mQueuedSender);
-  }
+  mQueuedSender->Dispatch(mDocShell);
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
      "finished", this));
 }
 
 void
 IMEContentObserver::TryToFlushPendingNotifications()
 {
-  if (!mQueuedSender || mSendingNotification != NOTIFY_IME_OF_NOTHING) {
+  if (!mQueuedSender || mSendingNotification != NOTIFY_IME_OF_NOTHING ||
+      XRE_IsContentProcess()) {
     return;
   }
 
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::TryToFlushPendingNotifications(), "
      "performing queued IMENotificationSender forcibly", this));
   RefPtr<IMENotificationSender> queuedSender = mQueuedSender;
   queuedSender->Run();
@@ -1762,273 +1756,300 @@ IMEContentObserver::TryToFlushPendingNot
 /******************************************************************************
  * mozilla::IMEContentObserver::AChangeEvent
  ******************************************************************************/
 
 bool
 IMEContentObserver::AChangeEvent::CanNotifyIME(
                                     ChangeEventType aChangeEventType) const
 {
-  if (NS_WARN_IF(!mIMEContentObserver)) {
+  RefPtr<IMEContentObserver> observer = GetObserver();
+  if (NS_WARN_IF(!observer)) {
     return false;
   }
+
   if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
-    return mIMEContentObserver->mWidget != nullptr;
+    return observer->mWidget != nullptr;
   }
-  State state = mIMEContentObserver->GetState();
+  State state = observer->GetState();
   // If it's not initialized, we should do nothing.
   if (state == eState_NotObserving) {
     return false;
   }
   // If setting focus, just check the state.
   if (aChangeEventType == eChangeEventType_Focus) {
-    return !NS_WARN_IF(mIMEContentObserver->mIMEHasFocus);
+    return !NS_WARN_IF(observer->mIMEHasFocus);
   }
   // If we've not notified IME of focus yet, we shouldn't notify anything.
-  if (!mIMEContentObserver->mIMEHasFocus) {
+  if (!observer->mIMEHasFocus) {
     return false;
   }
 
   // If IME has focus, IMEContentObserver must hold the widget.
-  MOZ_ASSERT(mIMEContentObserver->mWidget);
+  MOZ_ASSERT(observer->mWidget);
 
   return true;
 }
 
 bool
 IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(
                                     ChangeEventType aChangeEventType) const
 {
   if (NS_WARN_IF(!nsContentUtils::IsSafeToRunScript())) {
     return false;
   }
+
+  RefPtr<IMEContentObserver> observer = GetObserver();
+  if (!observer) {
+    return false;
+  }
+
   // While we're sending a notification, we shouldn't send another notification
   // recursively.
-  if (mIMEContentObserver->mSendingNotification != NOTIFY_IME_OF_NOTHING) {
+  if (observer->mSendingNotification != NOTIFY_IME_OF_NOTHING) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p   IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(), "
        "putting off sending notification due to detecting recursive call, "
        "mIMEContentObserver={ mSendingNotification=%s }",
-       this, ToChar(mIMEContentObserver->mSendingNotification)));
+       this, ToChar(observer->mSendingNotification)));
     return false;
   }
-  State state = mIMEContentObserver->GetState();
+  State state = observer->GetState();
   if (aChangeEventType == eChangeEventType_Focus) {
     if (NS_WARN_IF(state != eState_Initializing && state != eState_Observing)) {
       return false;
     }
   } else if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
     // It doesn't need to check the observing status.
   } else if (state != eState_Observing) {
     return false;
   }
-  return mIMEContentObserver->IsSafeToNotifyIME();
+  return observer->IsSafeToNotifyIME();
 }
 
 /******************************************************************************
  * mozilla::IMEContentObserver::IMENotificationSender
  ******************************************************************************/
- 
+
+void
+IMEContentObserver::IMENotificationSender::Dispatch(nsIDocShell* aDocShell)
+{
+  if (XRE_IsContentProcess() && aDocShell) {
+    RefPtr<nsPresContext> presContext;
+    aDocShell->GetPresContext(getter_AddRefs(presContext));
+    if (presContext) {
+      nsRefreshDriver* refreshDriver = presContext->RefreshDriver();
+      if (refreshDriver) {
+        refreshDriver->AddEarlyRunner(this);
+        return;
+      }
+    }
+  }
+
+  nsIScriptGlobalObject* globalObject =
+    aDocShell ? aDocShell->GetScriptGlobalObject() : nullptr;
+  if (globalObject) {
+    RefPtr<IMENotificationSender> queuedSender = this;
+    globalObject->Dispatch(nullptr, TaskCategory::Other,
+                           queuedSender.forget());
+  } else {
+    NS_DispatchToCurrentThread(this);
+  }
+}
+
 NS_IMETHODIMP
 IMEContentObserver::IMENotificationSender::Run()
 {
   if (NS_WARN_IF(mIsRunning)) {
     MOZ_LOG(sIMECOLog, LogLevel::Error,
       ("0x%p IMEContentObserver::IMENotificationSender::Run(), FAILED, "
        "called recursively", this));
     return NS_OK;
   }
 
+  RefPtr<IMEContentObserver> observer = GetObserver();
+  if (!observer) {
+    return NS_OK;
+  }
+
   AutoRestore<bool> running(mIsRunning);
   mIsRunning = true;
 
   // This instance was already performed forcibly.
-  if (mIMEContentObserver->mQueuedSender != this) {
+  if (observer->mQueuedSender != this) {
     return NS_OK;
   }
 
   // NOTE: Reset each pending flag because sending notification may cause
   //       another change.
 
-  if (mIMEContentObserver->mNeedsToNotifyIMEOfFocusSet) {
-    mIMEContentObserver->mNeedsToNotifyIMEOfFocusSet = false;
+  if (observer->mNeedsToNotifyIMEOfFocusSet) {
+    observer->mNeedsToNotifyIMEOfFocusSet = false;
     SendFocusSet();
-    mIMEContentObserver->mQueuedSender = nullptr;
+    observer->mQueuedSender = nullptr;
     // If it's not safe to notify IME of focus, SendFocusSet() sets
     // mNeedsToNotifyIMEOfFocusSet true again.  For guaranteeing to send the
     // focus notification later,  we should put a new sender into the queue but
     // this case must be rare.  Note that if mIMEContentObserver is already
     // destroyed, mNeedsToNotifyIMEOfFocusSet is never set true again.
-    if (mIMEContentObserver->mNeedsToNotifyIMEOfFocusSet) {
-      MOZ_ASSERT(!mIMEContentObserver->mIMEHasFocus);
+    if (observer->mNeedsToNotifyIMEOfFocusSet) {
+      MOZ_ASSERT(!observer->mIMEHasFocus);
       MOZ_LOG(sIMECOLog, LogLevel::Debug,
         ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
          "posting IMENotificationSender to current thread", this));
-      mIMEContentObserver->mQueuedSender =
-        new IMENotificationSender(mIMEContentObserver);
-      nsIScriptGlobalObject* globalObject =
-        mIMEContentObserver->mDocShell ?
-        mIMEContentObserver->mDocShell->GetScriptGlobalObject() : nullptr;
-      if (globalObject) {
-        RefPtr<IMENotificationSender> queuedSender =
-          mIMEContentObserver->mQueuedSender;
-        globalObject->Dispatch(nullptr, TaskCategory::Other,
-                               queuedSender.forget());
-      } else {
-        NS_DispatchToCurrentThread(mIMEContentObserver->mQueuedSender);
-      }
+      observer->mQueuedSender = new IMENotificationSender(observer);
+      observer->mQueuedSender->Dispatch(observer->mDocShell);
       return NS_OK;
     }
     // This is the first notification to IME. So, we don't need to notify
     // anymore since IME starts to query content after it gets focus.
-    mIMEContentObserver->ClearPendingNotifications();
+    observer->ClearPendingNotifications();
     return NS_OK;
   }
 
-  if (mIMEContentObserver->mNeedsToNotifyIMEOfTextChange) {
-    mIMEContentObserver->mNeedsToNotifyIMEOfTextChange = false;
+  if (observer->mNeedsToNotifyIMEOfTextChange) {
+    observer->mNeedsToNotifyIMEOfTextChange = false;
     SendTextChange();
   }
 
   // If a text change notification causes another text change again, we should
   // notify IME of that before sending a selection change notification.
-  if (!mIMEContentObserver->mNeedsToNotifyIMEOfTextChange) {
+  if (!observer->mNeedsToNotifyIMEOfTextChange) {
     // Be aware, PuppetWidget depends on the order of this. A selection change
     // notification should not be sent before a text change notification because
     // PuppetWidget shouldn't query new text content every selection change.
-    if (mIMEContentObserver->mNeedsToNotifyIMEOfSelectionChange) {
-      mIMEContentObserver->mNeedsToNotifyIMEOfSelectionChange = false;
+    if (observer->mNeedsToNotifyIMEOfSelectionChange) {
+      observer->mNeedsToNotifyIMEOfSelectionChange = false;
       SendSelectionChange();
     }
   }
 
   // If a text change notification causes another text change again or a
   // selection change notification causes either a text change or another
   // selection change, we should notify IME of those before sending a position
   // change notification.
-  if (!mIMEContentObserver->mNeedsToNotifyIMEOfTextChange &&
-      !mIMEContentObserver->mNeedsToNotifyIMEOfSelectionChange) {
-    if (mIMEContentObserver->mNeedsToNotifyIMEOfPositionChange) {
-      mIMEContentObserver->mNeedsToNotifyIMEOfPositionChange = false;
+  if (!observer->mNeedsToNotifyIMEOfTextChange &&
+      !observer->mNeedsToNotifyIMEOfSelectionChange) {
+    if (observer->mNeedsToNotifyIMEOfPositionChange) {
+      observer->mNeedsToNotifyIMEOfPositionChange = false;
       SendPositionChange();
     }
   }
 
   // Composition event handled notification should be sent after all the
   // other notifications because this notifies widget of finishing all pending
   // events are handled completely.
-  if (!mIMEContentObserver->mNeedsToNotifyIMEOfTextChange &&
-      !mIMEContentObserver->mNeedsToNotifyIMEOfSelectionChange &&
-      !mIMEContentObserver->mNeedsToNotifyIMEOfPositionChange) {
-    if (mIMEContentObserver->mNeedsToNotifyIMEOfCompositionEventHandled) {
-      mIMEContentObserver->mNeedsToNotifyIMEOfCompositionEventHandled = false;
+  if (!observer->mNeedsToNotifyIMEOfTextChange &&
+      !observer->mNeedsToNotifyIMEOfSelectionChange &&
+      !observer->mNeedsToNotifyIMEOfPositionChange) {
+    if (observer->mNeedsToNotifyIMEOfCompositionEventHandled) {
+      observer->mNeedsToNotifyIMEOfCompositionEventHandled = false;
       SendCompositionEventHandled();
     }
   }
 
-  mIMEContentObserver->mQueuedSender = nullptr;
+  observer->mQueuedSender = nullptr;
 
   // If notifications caused some new change, we should notify them now.
-  if (mIMEContentObserver->NeedsToNotifyIMEOfSomething()) {
-    if (mIMEContentObserver->GetState() == eState_StoppedObserving) {
+  if (observer->NeedsToNotifyIMEOfSomething()) {
+    if (observer->GetState() == eState_StoppedObserving) {
       MOZ_LOG(sIMECOLog, LogLevel::Debug,
         ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
          "waiting IMENotificationSender to be reinitialized", this));
     } else {
       MOZ_LOG(sIMECOLog, LogLevel::Debug,
         ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
          "posting IMENotificationSender to current thread", this));
-      mIMEContentObserver->mQueuedSender =
-        new IMENotificationSender(mIMEContentObserver);
-      nsIScriptGlobalObject* globalObject =
-        mIMEContentObserver->mDocShell ?
-        mIMEContentObserver->mDocShell->GetScriptGlobalObject() : nullptr;
-      if (globalObject) {
-        RefPtr<IMENotificationSender> queuedSender =
-          mIMEContentObserver->mQueuedSender;
-        globalObject->Dispatch(nullptr, TaskCategory::Other,
-                               queuedSender.forget());
-      } else {
-        NS_DispatchToCurrentThread(mIMEContentObserver->mQueuedSender);
-      }
+      observer->mQueuedSender = new IMENotificationSender(observer);
+      observer->mQueuedSender->Dispatch(observer->mDocShell);
     }
   }
   return NS_OK;
 }
 
 void
 IMEContentObserver::IMENotificationSender::SendFocusSet()
 {
+  RefPtr<IMEContentObserver> observer = GetObserver();
+  if (!observer) {
+    return;
+  }
+
   if (!CanNotifyIME(eChangeEventType_Focus)) {
     // If IMEContentObserver has already gone, we don't need to notify IME of
     // focus.
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p IMEContentObserver::IMENotificationSender::"
        "SendFocusSet(), FAILED, due to impossible to notify IME of focus",
        this));
-    mIMEContentObserver->ClearPendingNotifications();
+    observer->ClearPendingNotifications();
     return;
   }
 
   if (!IsSafeToNotifyIME(eChangeEventType_Focus)) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p   IMEContentObserver::IMENotificationSender::"
        "SendFocusSet(), retrying to send NOTIFY_IME_OF_FOCUS...", this));
-    mIMEContentObserver->PostFocusSetNotification();
+    observer->PostFocusSetNotification();
     return;
   }
 
-  mIMEContentObserver->mIMEHasFocus = true;
+  observer->mIMEHasFocus = true;
   // Initialize selection cache with the first selection data.
-  mIMEContentObserver->UpdateSelectionCache();
+  observer->UpdateSelectionCache();
 
   MOZ_LOG(sIMECOLog, LogLevel::Info,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendFocusSet(), sending NOTIFY_IME_OF_FOCUS...", this));
 
-  MOZ_RELEASE_ASSERT(mIMEContentObserver->mSendingNotification ==
+  MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
                        NOTIFY_IME_OF_NOTHING);
-  mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_FOCUS;
+  observer->mSendingNotification = NOTIFY_IME_OF_FOCUS;
   IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS),
-                             mIMEContentObserver->mWidget);
-  mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_NOTHING;
+                             observer->mWidget);
+  observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
 
   // IMENotificationRequests referred by ObserveEditableNode() may be different
   // before or after widget receives NOTIFY_IME_OF_FOCUS.  Therefore, we need
   // to guarantee to call ObserveEditableNode() after sending
   // NOTIFY_IME_OF_FOCUS.
-  mIMEContentObserver->OnIMEReceivedFocus();
+  observer->OnIMEReceivedFocus();
 
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendFocusSet(), sent NOTIFY_IME_OF_FOCUS", this));
 }
 
 void
 IMEContentObserver::IMENotificationSender::SendSelectionChange()
 {
+  RefPtr<IMEContentObserver> observer = GetObserver();
+  if (!observer) {
+    return;
+  }
+
   if (!CanNotifyIME(eChangeEventType_Selection)) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p IMEContentObserver::IMENotificationSender::"
        "SendSelectionChange(), FAILED, due to impossible to notify IME of "
        "selection change", this));
     return;
   }
 
   if (!IsSafeToNotifyIME(eChangeEventType_Selection)) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p   IMEContentObserver::IMENotificationSender::"
        "SendSelectionChange(), retrying to send "
        "NOTIFY_IME_OF_SELECTION_CHANGE...", this));
-    mIMEContentObserver->PostSelectionChangeNotification();
+    observer->PostSelectionChangeNotification();
     return;
   }
 
-  SelectionChangeData lastSelChangeData = mIMEContentObserver->mSelectionData;
-  if (NS_WARN_IF(!mIMEContentObserver->UpdateSelectionCache())) {
+  SelectionChangeData lastSelChangeData = observer->mSelectionData;
+  if (NS_WARN_IF(!observer->UpdateSelectionCache())) {
     MOZ_LOG(sIMECOLog, LogLevel::Error,
       ("0x%p IMEContentObserver::IMENotificationSender::"
        "SendSelectionChange(), FAILED, due to UpdateSelectionCache() failure",
        this));
     return;
   }
 
   // The state may be changed since querying content causes flushing layout.
@@ -2037,17 +2058,17 @@ IMEContentObserver::IMENotificationSende
       ("0x%p IMEContentObserver::IMENotificationSender::"
        "SendSelectionChange(), FAILED, due to flushing layout having changed "
        "something", this));
     return;
   }
 
   // If the selection isn't changed actually, we shouldn't notify IME of
   // selection change.
-  SelectionChangeData& newSelChangeData = mIMEContentObserver->mSelectionData;
+  SelectionChangeData& newSelChangeData = observer->mSelectionData;
   if (lastSelChangeData.IsValid() &&
       lastSelChangeData.mOffset == newSelChangeData.mOffset &&
       lastSelChangeData.String() == newSelChangeData.String() &&
       lastSelChangeData.GetWritingMode() == newSelChangeData.GetWritingMode() &&
       lastSelChangeData.mReversed == newSelChangeData.mReversed) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p IMEContentObserver::IMENotificationSender::"
        "SendSelectionChange(), not notifying IME of "
@@ -2057,159 +2078,174 @@ IMEContentObserver::IMENotificationSende
 
   MOZ_LOG(sIMECOLog, LogLevel::Info,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendSelectionChange(), sending NOTIFY_IME_OF_SELECTION_CHANGE... "
      "newSelChangeData=%s",
      this, SelectionChangeDataToString(newSelChangeData).get()));
 
   IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
-  notification.SetData(mIMEContentObserver->mSelectionData);
+  notification.SetData(observer->mSelectionData);
 
-  MOZ_RELEASE_ASSERT(mIMEContentObserver->mSendingNotification ==
+  MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
                        NOTIFY_IME_OF_NOTHING);
-  mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_SELECTION_CHANGE;
-  IMEStateManager::NotifyIME(notification, mIMEContentObserver->mWidget);
-  mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_NOTHING;
+  observer->mSendingNotification = NOTIFY_IME_OF_SELECTION_CHANGE;
+  IMEStateManager::NotifyIME(notification, observer->mWidget);
+  observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
 
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendSelectionChange(), sent NOTIFY_IME_OF_SELECTION_CHANGE", this));
 }
 
 void
 IMEContentObserver::IMENotificationSender::SendTextChange()
 {
+  RefPtr<IMEContentObserver> observer = GetObserver();
+  if (!observer) {
+    return;
+  }
+
   if (!CanNotifyIME(eChangeEventType_Text)) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p IMEContentObserver::IMENotificationSender::"
        "SendTextChange(), FAILED, due to impossible to notify IME of text "
        "change", this));
     return;
   }
 
   if (!IsSafeToNotifyIME(eChangeEventType_Text)) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p   IMEContentObserver::IMENotificationSender::"
        "SendTextChange(), retrying to send NOTIFY_IME_OF_TEXT_CHANGE...",
        this));
-    mIMEContentObserver->PostTextChangeNotification();
+    observer->PostTextChangeNotification();
     return;
   }
 
   // If text change notification is unnecessary anymore, just cancel it.
-  if (!mIMEContentObserver->NeedsTextChangeNotification()) {
+  if (!observer->NeedsTextChangeNotification()) {
     MOZ_LOG(sIMECOLog, LogLevel::Warning,
       ("0x%p   IMEContentObserver::IMENotificationSender::"
        "SendTextChange(), canceling sending NOTIFY_IME_OF_TEXT_CHANGE",
        this));
-    mIMEContentObserver->CancelNotifyingIMEOfTextChange();
+    observer->CancelNotifyingIMEOfTextChange();
     return;
   }
 
   MOZ_LOG(sIMECOLog, LogLevel::Info,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendTextChange(), sending NOTIFY_IME_OF_TEXT_CHANGE... "
      "mIMEContentObserver={ mTextChangeData=%s }",
-     this, TextChangeDataToString(mIMEContentObserver->mTextChangeData).get()));
+     this, TextChangeDataToString(observer->mTextChangeData).get()));
 
   IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
-  notification.SetData(mIMEContentObserver->mTextChangeData);
-  mIMEContentObserver->mTextChangeData.Clear();
+  notification.SetData(observer->mTextChangeData);
+  observer->mTextChangeData.Clear();
 
-  MOZ_RELEASE_ASSERT(mIMEContentObserver->mSendingNotification ==
+  MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
                        NOTIFY_IME_OF_NOTHING);
-  mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_TEXT_CHANGE;
-  IMEStateManager::NotifyIME(notification, mIMEContentObserver->mWidget);
-  mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_NOTHING;
+  observer->mSendingNotification = NOTIFY_IME_OF_TEXT_CHANGE;
+  IMEStateManager::NotifyIME(notification, observer->mWidget);
+  observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
 
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendTextChange(), sent NOTIFY_IME_OF_TEXT_CHANGE", this));
 }
 
 void
 IMEContentObserver::IMENotificationSender::SendPositionChange()
 {
+  RefPtr<IMEContentObserver> observer = GetObserver();
+  if (!observer) {
+    return;
+  }
+
   if (!CanNotifyIME(eChangeEventType_Position)) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p IMEContentObserver::IMENotificationSender::"
        "SendPositionChange(), FAILED, due to impossible to notify IME of "
        "position change", this));
     return;
   }
 
   if (!IsSafeToNotifyIME(eChangeEventType_Position)) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p   IMEContentObserver::IMENotificationSender::"
        "SendPositionChange(), retrying to send "
        "NOTIFY_IME_OF_POSITION_CHANGE...", this));
-    mIMEContentObserver->PostPositionChangeNotification();
+    observer->PostPositionChangeNotification();
     return;
   }
 
   // If position change notification is unnecessary anymore, just cancel it.
-  if (!mIMEContentObserver->NeedsPositionChangeNotification()) {
+  if (!observer->NeedsPositionChangeNotification()) {
     MOZ_LOG(sIMECOLog, LogLevel::Warning,
       ("0x%p   IMEContentObserver::IMENotificationSender::"
        "SendPositionChange(), canceling sending NOTIFY_IME_OF_POSITION_CHANGE",
        this));
-    mIMEContentObserver->CancelNotifyingIMEOfPositionChange();
+    observer->CancelNotifyingIMEOfPositionChange();
     return;
   }
 
   MOZ_LOG(sIMECOLog, LogLevel::Info,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendPositionChange(), sending NOTIFY_IME_OF_POSITION_CHANGE...", this));
 
-  MOZ_RELEASE_ASSERT(mIMEContentObserver->mSendingNotification ==
+  MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
                        NOTIFY_IME_OF_NOTHING);
-  mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_POSITION_CHANGE;
+  observer->mSendingNotification = NOTIFY_IME_OF_POSITION_CHANGE;
   IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_POSITION_CHANGE),
-                             mIMEContentObserver->mWidget);
-  mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_NOTHING;
+                             observer->mWidget);
+  observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
 
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendPositionChange(), sent NOTIFY_IME_OF_POSITION_CHANGE", this));
 }
 
 void
 IMEContentObserver::IMENotificationSender::SendCompositionEventHandled()
 {
+  RefPtr<IMEContentObserver> observer = GetObserver();
+  if (!observer) {
+    return;
+  }
+
   if (!CanNotifyIME(eChangeEventType_CompositionEventHandled)) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p IMEContentObserver::IMENotificationSender::"
        "SendCompositionEventHandled(), FAILED, due to impossible to notify "
        "IME of composition event handled", this));
     return;
   }
 
   if (!IsSafeToNotifyIME(eChangeEventType_CompositionEventHandled)) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p   IMEContentObserver::IMENotificationSender::"
        "SendCompositionEventHandled(), retrying to send "
        "NOTIFY_IME_OF_POSITION_CHANGE...", this));
-    mIMEContentObserver->PostCompositionEventHandledNotification();
+    observer->PostCompositionEventHandledNotification();
     return;
   }
 
   MOZ_LOG(sIMECOLog, LogLevel::Info,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendCompositionEventHandled(), sending "
      "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED...", this));
 
-  MOZ_RELEASE_ASSERT(mIMEContentObserver->mSendingNotification ==
+  MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
                        NOTIFY_IME_OF_NOTHING);
-  mIMEContentObserver->mSendingNotification =
+  observer->mSendingNotification =
                          NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED;
   IMEStateManager::NotifyIME(
                      IMENotification(NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED),
-                     mIMEContentObserver->mWidget);
-  mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_NOTHING;
+                     observer->mWidget);
+  observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
 
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendCompositionEventHandled(), sent "
      "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED", this));
 }
 
 /******************************************************************************
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -149,16 +149,18 @@ public:
   void UnsuppressNotifyingIME();
   nsPresContext* GetPresContext() const;
   nsresult GetSelectionAndRoot(nsISelection** aSelection,
                                nsIContent** aRoot) const;
 
   /**
    * TryToFlushPendingNotifications() should be called when pending events
    * should be flushed.  This tries to run the queued IMENotificationSender.
+   * Doesn't do anything in child processes where flushing happens
+   * asynchronously.
    */
   void TryToFlushPendingNotifications();
 
   /**
    * MaybeNotifyCompositionEventHandled() posts composition event handled
    * notification into the pseudo queue.
    */
   void MaybeNotifyCompositionEventHandled();
@@ -326,22 +328,31 @@ private:
       eChangeEventType_Text,
       eChangeEventType_Position,
       eChangeEventType_CompositionEventHandled
     };
 
     explicit AChangeEvent(const char* aName,
                           IMEContentObserver* aIMEContentObserver)
       : Runnable(aName)
-      , mIMEContentObserver(aIMEContentObserver)
+      , mIMEContentObserver(
+          do_GetWeakReference(
+            static_cast<nsISelectionListener*>(aIMEContentObserver)))
     {
-      MOZ_ASSERT(mIMEContentObserver);
+      MOZ_ASSERT(aIMEContentObserver);
     }
 
-    RefPtr<IMEContentObserver> mIMEContentObserver;
+    already_AddRefed<IMEContentObserver> GetObserver() const
+    {
+      nsCOMPtr<nsISelectionListener> observer =
+        do_QueryReferent(mIMEContentObserver);
+      return observer.forget().downcast<IMEContentObserver>();
+    }
+
+    nsWeakPtr mIMEContentObserver;
 
     /**
      * CanNotifyIME() checks if mIMEContentObserver can and should notify IME.
      */
     bool CanNotifyIME(ChangeEventType aChangeEventType) const;
 
     /**
      * IsSafeToNotifyIME() checks if it's safe to noitify IME.
@@ -354,16 +365,17 @@ private:
   public:
     explicit IMENotificationSender(IMEContentObserver* aIMEContentObserver)
       : AChangeEvent("IMENotificationSender", aIMEContentObserver)
       , mIsRunning(false)
     {
     }
     NS_IMETHOD Run() override;
 
+    void Dispatch(nsIDocShell* aDocShell);
   private:
     void SendFocusSet();
     void SendSelectionChange();
     void SendTextChange();
     void SendPositionChange();
     void SendCompositionEventHandled();
 
     bool mIsRunning;
--- a/dom/events/UIEvent.cpp
+++ b/dom/events/UIEvent.cpp
@@ -239,17 +239,21 @@ UIEvent::GetWhich(uint32_t* aWhich)
 }
 
 already_AddRefed<nsINode>
 UIEvent::GetRangeParent()
 {
   nsIFrame* targetFrame = nullptr;
 
   if (mPresContext) {
-    targetFrame = mPresContext->EventStateManager()->GetEventTarget();
+    nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
+    if (shell) {
+      shell->FlushPendingNotifications(FlushType::Layout);
+      targetFrame = mPresContext->EventStateManager()->GetEventTarget();
+    }
   }
 
   if (targetFrame) {
     nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
                                                               targetFrame);
     nsCOMPtr<nsIContent> parent = targetFrame->GetContentOffsetsFromPoint(pt).content;
     if (parent) {
       if (parent->ChromeOnlyAccess() &&
@@ -285,16 +289,23 @@ UIEvent::GetRangeOffset(int32_t* aRangeO
 
 int32_t
 UIEvent::RangeOffset() const
 {
   if (!mPresContext) {
     return 0;
   }
 
+  nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
+  if (!shell) {
+    return 0;
+  }
+
+  shell->FlushPendingNotifications(FlushType::Layout);
+
   nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget();
   if (!targetFrame) {
     return 0;
   }
 
   nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
                                                             targetFrame);
   return targetFrame->GetContentOffsetsFromPoint(pt).offset;
--- a/editor/libeditor/tests/test_bug551704.html
+++ b/editor/libeditor/tests/test_bug551704.html
@@ -27,33 +27,35 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 
 function testLineBreak(div, type, expectedText, expectedHTML, callback)
 {
   div.focus();
   getSelection().collapse(div, 0);
   type();
   is(div.innerHTML, expectedHTML, "The expected HTML after editing should be correct");
-  SimpleTest.waitForClipboard(expectedText,
-    function() {
-      getSelection().selectAllChildren(div);
-      synthesizeKey("C", {accelKey: true});
-    },
-    function() {
-      var t = document.createElement("textarea");
-      document.body.appendChild(t);
-      t.focus();
-      synthesizeKey("V", {accelKey: true});
-      is(t.value, expectedText, "The expected text should be copied to the clipboard");
-      callback();
-    },
-    function() {
-      SimpleTest.finish();
-    }
-  );
+  requestAnimationFrame(function() {
+    SimpleTest.waitForClipboard(expectedText,
+      function() {
+        getSelection().selectAllChildren(div);
+        synthesizeKey("C", {accelKey: true});
+      },
+      function() {
+        var t = document.createElement("textarea");
+        document.body.appendChild(t);
+        t.focus();
+        synthesizeKey("V", {accelKey: true});
+        is(t.value, expectedText, "The expected text should be copied to the clipboard");
+        callback();
+      },
+      function() {
+        SimpleTest.finish();
+      }
+    );
+  });
 }
 
 function typeABCDEF() {
   synthesizeKey("a", {});
   typeBCDEF_chars();
 }
 
 function typeBCDEF() {
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1209,17 +1209,17 @@ nsRefreshDriver::nsRefreshDriver(nsPresC
   mNextRecomputeVisibilityTick = mMostRecentTick;
 
   ++sRefreshDriverCount;
 }
 
 nsRefreshDriver::~nsRefreshDriver()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(ObserverCount() == mPendingSelectionScrolls.Length(),
+  MOZ_ASSERT(ObserverCount() == mEarlyRunners.Length(),
              "observers, except pending selection scrolls, "
              "should have been unregistered");
   MOZ_ASSERT(!mActiveTimer, "timer should be gone");
   MOZ_ASSERT(!mPresContext,
              "Should have called Disconnect() and decremented "
              "sRefreshDriverCount!");
 
   if (mRootRefresh) {
@@ -1447,17 +1447,17 @@ nsRefreshDriver::ObserverCount() const
   // layout changes can affect media queries on child documents, triggering
   // style changes, etc.
   sum += mStyleFlushObservers.Length();
   sum += mLayoutFlushObservers.Length();
   sum += mPendingEvents.Length();
   sum += mFrameRequestCallbackDocs.Length();
   sum += mThrottledFrameRequestCallbackDocs.Length();
   sum += mViewManagerFlushIsPending;
-  sum += mPendingSelectionScrolls.Length();
+  sum += mEarlyRunners.Length();
   return sum;
 }
 
 uint32_t
 nsRefreshDriver::ImageRequestCount() const
 {
   uint32_t count = 0;
   for (auto iter = mStartTable.ConstIter(); !iter.Done(); iter.Next()) {
@@ -1835,20 +1835,20 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
 
   // We want to process any pending APZ metrics ahead of their positions
   // in the queue. This will prevent us from spending precious time
   // painting a stale displayport.
   if (gfxPrefs::APZPeekMessages()) {
     nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages();
   }
 
-  AutoTArray<nsCOMPtr<nsIRunnable>, 16> pendingSelectionScrolls;
-  pendingSelectionScrolls.SwapElements(mPendingSelectionScrolls);
-  for (uint32_t i = 0; i < pendingSelectionScrolls.Length(); ++i) {
-    pendingSelectionScrolls[i]->Run();
+  AutoTArray<nsCOMPtr<nsIRunnable>, 16> earlyRunners;
+  earlyRunners.SwapElements(mEarlyRunners);
+  for (uint32_t i = 0; i < earlyRunners.Length(); ++i) {
+    earlyRunners[i]->Run();
   }
 
   /*
    * The timer holds a reference to |this| while calling |Notify|.
    * However, implementations of |WillRefresh| are permitted to destroy
    * the pres context, which will cause our |mPresContext| to become
    * null.  If this happens, we must stop notifying observers.
    */
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -187,19 +187,24 @@ public:
   }
   void RemoveLayoutFlushObserver(nsIPresShell* aShell) {
     mLayoutFlushObservers.RemoveElement(aShell);
   }
   bool IsLayoutFlushObserver(nsIPresShell* aShell) {
     return mLayoutFlushObservers.Contains(aShell);
   }
 
-  void AddPendingSelectionScroll(nsIRunnable* aRunnable)
+  /**
+   * "Early Runner" runnables will be called as the first step when refresh
+   * driver tick is triggered. Runners shouldn't keep other objects alive,
+   * since it isn't guaranteed they will ever get called.
+   */
+  void AddEarlyRunner(nsIRunnable* aRunnable)
   {
-    mPendingSelectionScrolls.AppendElement(aRunnable);
+    mEarlyRunners.AppendElement(aRunnable);
     EnsureTimerStarted();
   }
 
   /**
    * Remember whether our presshell's view manager needs a flush
    */
   void ScheduleViewManagerFlush();
   void RevokeViewManagerFlush() {
@@ -451,17 +456,17 @@ private:
   mozilla::TimeStamp mTickStart;
   mozilla::TimeStamp mNextThrottledFrameRequestTick;
   mozilla::TimeStamp mNextRecomputeVisibilityTick;
 
   // separate arrays for each flush type we support
   ObserverArray mObservers[3];
   RequestTable mRequests;
   ImageStartTable mStartTable;
-  AutoTArray<nsCOMPtr<nsIRunnable>, 16> mPendingSelectionScrolls;
+  AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners;
 
   struct PendingEvent {
     nsCOMPtr<nsINode> mTarget;
     nsCOMPtr<nsIDOMEvent> mEvent;
   };
 
   AutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
   AutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;