Bug 1358813 - avoid flushing layout when notifying IME of focus events, r=masayuki
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Thu, 11 Oct 2018 13:06:52 +0000
changeset 499150 90fd055a420277d91231bb79508e8bd69b3b86f8
parent 499149 0abcb61b9fc1d858ee127f49ddc2becaf98bf3c4
child 499151 8297dbe747658a324023c058342f713127926f1d
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs1358813
milestone64.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 1358813 - avoid flushing layout when notifying IME of focus events, r=masayuki Differential Revision: https://phabricator.services.mozilla.com/D8007
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
dom/events/IMEContentObserver.cpp
dom/events/IMEContentObserver.h
widget/TextEvents.h
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -242,22 +242,24 @@ ContentEventHandler::RawRange::SelectNod
 //    line break caused by the <br> should be included into the flatten text.
 
 ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext)
   : mDocument(aPresContext->Document())
 {
 }
 
 nsresult
-ContentEventHandler::InitBasic()
+ContentEventHandler::InitBasic(bool aRequireFlush)
 {
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
-  // If text frame which has overflowing selection underline is dirty,
-  // we need to flush the pending reflow here.
-  mDocument->FlushPendingNotifications(FlushType::Layout);
+  if (aRequireFlush) {
+    // If text frame which has overflowing selection underline is dirty,
+    // we need to flush the pending reflow here.
+    mDocument->FlushPendingNotifications(FlushType::Layout);
+  }
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::InitRootContent(Selection* aNormalSelection)
 {
   MOZ_ASSERT(aNormalSelection);
 
@@ -309,27 +311,27 @@ ContentEventHandler::InitRootContent(Sel
   if (NS_WARN_IF(!mRootContent)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
-ContentEventHandler::InitCommon(SelectionType aSelectionType)
+ContentEventHandler::InitCommon(SelectionType aSelectionType, bool aRequireFlush)
 {
   if (mSelection && mSelection->Type() == aSelectionType) {
     return NS_OK;
   }
 
   mSelection = nullptr;
   mRootContent = nullptr;
   mFirstSelectedRawRange.Clear();
 
-  nsresult rv = InitBasic();
+  nsresult rv = InitBasic(aRequireFlush);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsISelectionController> selectionController;
   if (nsIPresShell* shell = mDocument->GetShell()) {
     selectionController = shell->GetSelectionControllerForFocusedContent();
   }
   if (NS_WARN_IF(!selectionController)) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -394,17 +396,17 @@ ContentEventHandler::Init(WidgetQueryCon
   // if the event isn't eQuerySelectedText.
   SelectionType selectionType =
     aEvent->mMessage == eQuerySelectedText ? aEvent->mInput.mSelectionType :
                                              SelectionType::eNormal;
   if (NS_WARN_IF(selectionType == SelectionType::eNone)) {
     return NS_ERROR_FAILURE;
   }
 
-  nsresult rv = InitCommon(selectionType);
+  nsresult rv = InitCommon(selectionType, aEvent->NeedsToFlushLayout());
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Be aware, WidgetQueryContentEvent::mInput::mOffset should be made absolute
   // offset before sending it to ContentEventHandler because querying selection
   // every time may be expensive.  So, if the caller caches selection, it
   // should initialize the event with the cached value.
   if (aEvent->mInput.mRelativeToInsertionPoint) {
     MOZ_ASSERT(selectionType == SelectionType::eNormal);
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -148,18 +148,19 @@ protected:
   // mFirstSelectedRawRange is initialized from the first range of mSelection,
   // if it exists.  Otherwise, it is reset by Clear().
   RawRange mFirstSelectedRawRange;
   nsCOMPtr<nsIContent> mRootContent;
 
   nsresult Init(WidgetQueryContentEvent* aEvent);
   nsresult Init(WidgetSelectionEvent* aEvent);
 
-  nsresult InitBasic();
-  nsresult InitCommon(SelectionType aSelectionType = SelectionType::eNormal);
+  nsresult InitBasic(bool aRequireFlush = true);
+  nsresult InitCommon(SelectionType aSelectionType = SelectionType::eNormal,
+                      bool aRequireFlush = true);
   /**
    * InitRootContent() computes the root content of current focused editor.
    *
    * @param aNormalSelection    This must be a Selection instance whose type is
    *                            SelectionType::eNormal.
    */
   nsresult InitRootContent(Selection* aNormalSelection);
 
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -1542,29 +1542,30 @@ IMEContentObserver::MaybeNotifyCompositi
     ("0x%p IMEContentObserver::MaybeNotifyCompositionEventHandled()",
      this));
 
   PostCompositionEventHandledNotification();
   FlushMergeableNotifications();
 }
 
 bool
-IMEContentObserver::UpdateSelectionCache()
+IMEContentObserver::UpdateSelectionCache(bool aRequireFlush /* = true */)
 {
   MOZ_ASSERT(IsSafeToNotifyIME());
 
   if (WasInitializedWithPlugin()) {
     return false;
   }
 
   mSelectionData.ClearSelectionData();
 
   // XXX Cannot we cache some information for reducing the cost to compute
   //     selection offset and writing mode?
   WidgetQueryContentEvent selection(true, eQuerySelectedText, mWidget);
+  selection.mNeedsToFlushLayout = aRequireFlush;
   ContentEventHandler handler(GetPresContext());
   handler.OnQuerySelectedText(&selection);
   if (NS_WARN_IF(!selection.mSucceeded) ||
       NS_WARN_IF(selection.mReply.mContentsRoot != mRootContent)) {
     return false;
   }
 
   mFocusedWidget = selection.mReply.mFocusedWidget;
@@ -1961,17 +1962,18 @@ IMEContentObserver::IMENotificationSende
       ("0x%p   IMEContentObserver::IMENotificationSender::"
        "SendFocusSet(), retrying to send NOTIFY_IME_OF_FOCUS...", this));
     observer->PostFocusSetNotification();
     return;
   }
 
   observer->mIMEHasFocus = true;
   // Initialize selection cache with the first selection data.
-  observer->UpdateSelectionCache();
+  // We avoid flushing for focus in the general case.
+  observer->UpdateSelectionCache(false);
 
   MOZ_LOG(sIMECOLog, LogLevel::Info,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendFocusSet(), sending NOTIFY_IME_OF_FOCUS...", this));
 
   MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
                        NOTIFY_IME_OF_NOTHING);
   observer->mSendingNotification = NOTIFY_IME_OF_FOCUS;
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -302,17 +302,17 @@ private:
   }
 
   /**
    * UpdateSelectionCache() updates mSelectionData with the latest selection.
    * This should be called only when IsSafeToNotifyIME() returns true.
    *
    * Note that this does nothing if WasInitializedWithPlugin() returns true.
    */
-  bool UpdateSelectionCache();
+  bool UpdateSelectionCache(bool aRequireFlush = true);
 
   nsCOMPtr<nsIWidget> mWidget;
   // mFocusedWidget has the editor observed by the instance.  E.g., if the
   // focused editor is in XUL panel, this should be the widget of the panel.
   // On the other hand, mWidget is its parent which handles IME.
   nsCOMPtr<nsIWidget> mFocusedWidget;
   RefPtr<dom::Selection> mSelection;
   nsCOMPtr<nsIContent> mRootContent;
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -909,43 +909,46 @@ class WidgetQueryContentEvent : public W
 private:
   friend class dom::PBrowserParent;
   friend class dom::PBrowserChild;
 
   WidgetQueryContentEvent()
     : mSucceeded(false)
     , mUseNativeLineBreak(true)
     , mWithFontRanges(false)
+    , mNeedsToFlushLayout(true)
   {
     MOZ_CRASH("WidgetQueryContentEvent is created without proper arguments");
   }
 
 public:
   virtual WidgetQueryContentEvent* AsQueryContentEvent() override
   {
     return this;
   }
 
   WidgetQueryContentEvent(bool aIsTrusted, EventMessage aMessage,
                           nsIWidget* aWidget)
     : WidgetGUIEvent(aIsTrusted, aMessage, aWidget, eQueryContentEventClass)
     , mSucceeded(false)
     , mUseNativeLineBreak(true)
     , mWithFontRanges(false)
+    , mNeedsToFlushLayout(true)
   {
   }
 
   WidgetQueryContentEvent(EventMessage aMessage,
                           const WidgetQueryContentEvent& aOtherEvent)
     : WidgetGUIEvent(aOtherEvent.IsTrusted(), aMessage,
                      const_cast<nsIWidget*>(aOtherEvent.mWidget.get()),
                      eQueryContentEventClass)
     , mSucceeded(false)
     , mUseNativeLineBreak(aOtherEvent.mUseNativeLineBreak)
     , mWithFontRanges(false)
+    , mNeedsToFlushLayout(aOtherEvent.mNeedsToFlushLayout)
   {
   }
 
   virtual WidgetEvent* Duplicate() const override
   {
     // This event isn't an internal event of any DOM event.
     NS_ASSERTION(!IsAllowedToDispatchDOMEvent(),
       "WidgetQueryContentEvent needs to support Duplicate()");
@@ -1030,16 +1033,25 @@ public:
   {
     NS_ASSERTION(mMessage == eQueryTextRectArray,
                  "wrong initializer is called");
     mInput.mOffset = aOffset;
     mInput.mLength = aLength;
     Init(aOptions);
   }
 
+  bool NeedsToFlushLayout() const
+  {
+#ifdef XP_MACOSX
+    return true;
+#else
+    return mNeedsToFlushLayout;
+#endif
+  }
+
   void RequestFontRanges()
   {
     NS_ASSERTION(mMessage == eQueryTextContent,
                  "not querying text content");
     mWithFontRanges = true;
   }
 
   uint32_t GetSelectionStart(void) const
@@ -1063,16 +1075,17 @@ public:
                  mMessage == eQueryTextRect,
                  "not querying selection or text rect");
     return mReply.mWritingMode;
   }
 
   bool mSucceeded;
   bool mUseNativeLineBreak;
   bool mWithFontRanges;
+  bool mNeedsToFlushLayout;
   struct Input final
   {
     uint32_t EndOffset() const
     {
       CheckedInt<uint32_t> endOffset =
         CheckedInt<uint32_t>(mOffset) + mLength;
       return NS_WARN_IF(!endOffset.isValid()) ? UINT32_MAX : endOffset.value();
     }