Bug 1403759 - part 1: Make TextInputHandler::InsertNewline() treat other commands r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Sat, 02 Dec 2017 10:46:31 +0900
changeset 706528 6eb7f03e188cfc68c38b0159eff4b3fb6388a858
parent 705036 3f6b9aaed8cd57954e0c960cde06d25228196456
child 706529 94f26d0306914facfc0269fe8956435c30846f54
push id91820
push usermasayuki@d-toybox.com
push dateSat, 02 Dec 2017 06:06:47 +0000
reviewersm_kato
bugs1403759
milestone59.0a1
Bug 1403759 - part 1: Make TextInputHandler::InsertNewline() treat other commands r?m_kato Currently, we handle insertNewline: of NSResponder with TextInputHandler::InsertNewline(). However, its implementation is useful for handling some other commands. So, let's rename it to HandleCommand() and make it take Command as its argument for handling specific behavior of each command. MozReview-Commit-ID: GgzQdTlVtYl
widget/CommandList.h
widget/EventForwards.h
widget/WidgetEventImpl.cpp
widget/cocoa/TextInputHandler.h
widget/cocoa/TextInputHandler.mm
widget/cocoa/nsChildView.mm
--- a/widget/CommandList.h
+++ b/widget/CommandList.h
@@ -17,16 +17,18 @@ NS_DEFINE_COMMAND(Cut,                  
 NS_DEFINE_COMMAND(Delete,                      cmd_delete)
 NS_DEFINE_COMMAND(DeleteCharBackward,          cmd_deleteCharBackward)
 NS_DEFINE_COMMAND(DeleteCharForward,           cmd_deleteCharForward)
 NS_DEFINE_COMMAND(DeleteToBeginningOfLine,     cmd_deleteToBeginningOfLine)
 NS_DEFINE_COMMAND(DeleteToEndOfLine,           cmd_deleteToEndOfLine)
 NS_DEFINE_COMMAND(DeleteWordBackward,          cmd_deleteWordBackward)
 NS_DEFINE_COMMAND(DeleteWordForward,           cmd_deleteWordForward)
 NS_DEFINE_COMMAND(EndLine,                     cmd_endLine)
+NS_DEFINE_COMMAND(InsertParagraph,             cmd_insertParagraph)
+NS_DEFINE_COMMAND(InsertLineBreak,             cmd_insertLineBreak)
 NS_DEFINE_COMMAND(LineNext,                    cmd_lineNext)
 NS_DEFINE_COMMAND(LinePrevious,                cmd_linePrevious)
 NS_DEFINE_COMMAND(MoveBottom,                  cmd_moveBottom)
 NS_DEFINE_COMMAND(MovePageDown,                cmd_movePageDown)
 NS_DEFINE_COMMAND(MovePageUp,                  cmd_movePageUp)
 NS_DEFINE_COMMAND(MoveTop,                     cmd_moveTop)
 NS_DEFINE_COMMAND(Paste,                       cmd_paste)
 NS_DEFINE_COMMAND(ScrollBottom,                cmd_scrollBottom)
--- a/widget/EventForwards.h
+++ b/widget/EventForwards.h
@@ -119,16 +119,18 @@ typedef int8_t CommandInt;
 enum Command : CommandInt
 {
   CommandDoNothing
 
 #include "mozilla/CommandList.h"
 };
 #undef NS_DEFINE_COMMAND
 
+const char* ToChar(Command aCommand);
+
 } // namespace mozilla
 
 /**
  * All header files should include this header instead of *Events.h.
  */
 
 namespace mozilla {
 
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -80,16 +80,38 @@ ToString(CodeNameIndex aCodeNameIndex)
   if (aCodeNameIndex == CODE_NAME_INDEX_USE_STRING) {
     return NS_LITERAL_CSTRING("USE_STRING");
   }
   nsAutoString codeName;
   WidgetKeyboardEvent::GetDOMCodeName(aCodeNameIndex, codeName);
   return NS_ConvertUTF16toUTF8(codeName);
 }
 
+const char*
+ToChar(Command aCommand)
+{
+  if (aCommand == CommandDoNothing) {
+    return "CommandDoNothing";
+  }
+
+  switch (aCommand) {
+
+#define NS_DEFINE_COMMAND(aName, aCommandStr) \
+    case Command##aName: \
+      return "Command" #aName;
+
+#include "mozilla/CommandList.h"
+
+#undef NS_DEFINE_COMMAND
+
+    default:
+      return "illegal command value";
+  }
+}
+
 const nsCString
 GetDOMKeyCodeName(uint32_t aKeyCode)
 {
   switch (aKeyCode) {
 #define NS_DISALLOW_SAME_KEYCODE
 #define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
     case aDOMKeyCode: \
       return NS_LITERAL_CSTRING(#aDOMKeyName);
--- a/widget/cocoa/TextInputHandler.h
+++ b/widget/cocoa/TextInputHandler.h
@@ -1155,23 +1155,19 @@ public:
    * @param aAttrString           An inserted string.
    * @param aReplacementRange     The range which will be replaced with the
    *                              aAttrString instead of current selection.
    */
   void InsertText(NSAttributedString *aAttrString,
                   NSRange* aReplacementRange = nullptr);
 
   /**
-   * Handles "insertNewline:" command.  Due to bug 1350541, this always
-   * dispatches a keypress event of Enter key unless there is composition.
-   * If it's handling Enter key event, this dispatches "actual" Enter
-   * keypress event.  Otherwise, dispatches "fake" Enter keypress event
-   * whose code value is unidentified.
+   * Handles aCommand.  This may cause dispatching an eKeyPress event.
    */
-  void InsertNewline();
+  void HandleCommand(Command aCommand);
 
   /**
    * doCommandBySelector event handler.
    *
    * @param aSelector             A selector of the command.
    * @return                      TRUE if the command is consumed.  Otherwise,
    *                              FALSE.
    */
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -2346,110 +2346,132 @@ TextInputHandler::InsertText(NSAttribute
     currentKeyEvent->mKeyPressHandled = keyPressHandled;
     currentKeyEvent->mKeyPressDispatched = keyPressDispatched;
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 void
-TextInputHandler::InsertNewline()
+TextInputHandler::HandleCommand(Command aCommand)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if (Destroyed()) {
     return;
   }
 
   KeyEventState* currentKeyEvent = GetCurrentKeyEvent();
 
   MOZ_LOG(gLog, LogLevel::Info,
-    ("%p TextInputHandler::InsertNewline, "
-     "IsIMEComposing()=%s, "
+    ("%p TextInputHandler::HandleCommand, "
+     "aCommand=%s, IsIMEComposing()=%s, "
      "keyevent=%p, keydownHandled=%s, keypressDispatched=%s, "
      "causedOtherKeyEvents=%s, compositionDispatched=%s",
-     this, TrueOrFalse(IsIMEComposing()),
+     this, ToChar(aCommand), TrueOrFalse(IsIMEComposing()),
      currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr,
      currentKeyEvent ?
        TrueOrFalse(currentKeyEvent->mKeyDownHandled) : "N/A",
      currentKeyEvent ?
        TrueOrFalse(currentKeyEvent->mKeyPressDispatched) : "N/A",
      currentKeyEvent ?
        TrueOrFalse(currentKeyEvent->mCausedOtherKeyEvents) : "N/A",
      currentKeyEvent ?
        TrueOrFalse(currentKeyEvent->mCompositionDispatched) : "N/A"));
 
   // If "insertNewline:" command shouldn't be handled, let's ignore it.
   if (currentKeyEvent && !currentKeyEvent->CanHandleCommand()) {
     return;
   }
 
   // If it's in composition, we cannot dispatch keypress event.
-  // Therefore, we should insert '\n' as committing composition.
+  // Therefore, we should use different approach or give up to handle
+  // the command.
   if (IsIMEComposing()) {
-    NSAttributedString* lineBreaker =
-      [[NSAttributedString alloc] initWithString:@"\n"];
-    InsertTextAsCommittingComposition(lineBreaker, nullptr);
-    if (currentKeyEvent) {
-      currentKeyEvent->mCompositionDispatched = true;
+    switch (aCommand) {
+      case CommandInsertLineBreak:
+      case CommandInsertParagraph: {
+        // Insert '\n' as committing composition.
+        // Otherwise, we need to dispatch keypress event because HTMLEditor
+        // doesn't treat "\n" in composition string as a line break unless
+        // the whitespace is treated as pre (see bug 1350541).  In strictly
+        // speaking, we should dispatch keypress event as-is if it's handling
+        // NSKeyDown event or should insert it with committing composition.
+        NSAttributedString* lineBreaker =
+          [[NSAttributedString alloc] initWithString:@"\n"];
+        InsertTextAsCommittingComposition(lineBreaker, nullptr);
+        if (currentKeyEvent) {
+          currentKeyEvent->mCompositionDispatched = true;
+        }
+        [lineBreaker release];
+        return;
+      }
+      default:
+        break;
     }
-    [lineBreaker release];
-    return;
   }
 
-  // Otherwise, we need to dispatch keypress event because HTMLEditor doesn't
-  // treat "\n" in composition string as a line break unless the whitespace is
-  // treated as pre (see bug 1350541).  In strictly speaking, we should
-  // dispatch keypress event as-is if it's handling NSKeyDown event or
-  // should insert it with committing composition.
-
   RefPtr<nsChildView> widget(mWidget);
   nsresult rv = mDispatcher->BeginNativeInputTransaction();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     MOZ_LOG(gLog, LogLevel::Error,
-      ("%p, IMEInputHandler::InsertNewline, "
+      ("%p, IMEInputHandler::HandleCommand, "
        "FAILED, due to BeginNativeInputTransaction() failure", this));
     return;
   }
 
-  // TODO: If it's not Enter keypress but user customized the OS settings
-  //       to insert a line breaker with other key, we should just set
+  // TODO: If it's not appropriate keypress but user customized the OS
+  //       settings to do the command with other key, we should just set
   //       command to the keypress event and it should be handled as
-  //       Enter key press in editor.
-
-  // If it's handling actual Enter key event and hasn't cause any composition
+  //       the key press in editor.
+
+  // If it's handling actual key event and hasn't cause any composition
   // events nor other key events, we should expose actual modifier state.
-  // Otherwise, we should remove Control, Option and Command state since
-  // editor may behave differently if some of them are active.  Although,
-  // Shift+Enter and Enter are work differently in HTML editor, we should
-  // expose actual Shift state if it's caused by Enter key for compatibility
-  // with Chromium.  Chromium breaks line in HTML editor with default pargraph
-  // separator when Enter is pressed, with <br> element when Shift+Enter.
-  // Safari breaks line in HTML editor with default paragraph separator when
-  // Enter, Shift+Enter or Option+Enter.  So, we should not change Shift+Enter
-  // meaning when there was composition string or not.
+  // Otherwise, we should adjust Control, Option and Command state since
+  // editor may behave differently if some of them are active.
   bool dispatchFakeKeyPress =
     !(currentKeyEvent && currentKeyEvent->IsEnterKeyEvent() &&
       currentKeyEvent->CanDispatchKeyPressEvent());
 
   WidgetKeyboardEvent keypressEvent(true, eKeyPress, widget);
   if (!dispatchFakeKeyPress) {
-    // If we're acutally handling an Enter key press, we should dispatch
-    // Enter keypress event as-is.
+    // If we're acutally handling a key press, we should dispatch
+    // the keypress event as-is.
     currentKeyEvent->InitKeyEvent(this, keypressEvent);
   } else {
-    // Otherwise, we should dispatch "fake" Enter keypress event.
-    // In this case, we shouldn't set code value to "Enter".
-    NSEvent* keyEvent = currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr;
-    nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent);
-    keypressEvent.mKeyCode = NS_VK_RETURN;
-    keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_Enter;
-    keypressEvent.mModifiers &= ~(MODIFIER_CONTROL |
-                                  MODIFIER_ALT |
-                                  MODIFIER_META);
+    // Otherwise, we should dispatch "fake" keypress event.
+    switch (aCommand) {
+      case CommandInsertLineBreak:
+      case CommandInsertParagraph: {
+        // Although, Shift+Enter and Enter are work differently in HTML
+        // editor, we should expose actual Shift state if it's caused by
+        // Enter key for compatibility with Chromium.  Chromium breaks
+        // line in HTML editor with default pargraph separator when Enter
+        // is pressed, with <br> element when Shift+Enter.  Safari breaks
+        // line in HTML editor with default paragraph separator when
+        // Enter, Shift+Enter or Option+Enter.  So, we should not change
+        // Shift+Enter meaning when there was composition string or not.
+        NSEvent* keyEvent =
+          currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr;
+        nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent);
+        keypressEvent.mKeyCode = NS_VK_RETURN;
+        keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_Enter;
+        keypressEvent.mModifiers &= ~(MODIFIER_CONTROL |
+                                      MODIFIER_ALT |
+                                      MODIFIER_META);
+        if (aCommand == CommandInsertLineBreak) {
+          // In default settings, Ctrl + Enter causes insertLineBreak command.
+          // So, let's make Ctrl state active of the keypress event.
+          keypressEvent.mModifiers |= MODIFIER_CONTROL;
+        }
+        break;
+      }
+      default:
+        return;
+    }
   }
 
   nsEventStatus status = nsEventStatus_eIgnore;
   bool keyPressDispatched =
     mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, status,
                                              currentKeyEvent);
   bool keyPressHandled = (status == nsEventStatus_eConsumeNoDefault);
 
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -5582,17 +5582,25 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
   mTextInputHandler->HandleKeyUpEvent(theEvent);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)insertNewline:(id)sender
 {
   if (mTextInputHandler) {
-    mTextInputHandler->InsertNewline();
+    mTextInputHandler->HandleCommand(CommandInsertParagraph);
+  }
+}
+
+- (void)insertLineBreak:(id)sender
+{
+  // Ctrl + Enter in the default settings.
+  if (mTextInputHandler) {
+    mTextInputHandler->HandleCommand(CommandInsertLineBreak);
   }
 }
 
 - (void)flagsChanged:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   NS_ENSURE_TRUE(mGeckoChild, );