Bug 1119609 part.8 nsITextInputProcessor.keydown() and nsITextInputProcessor.keyup() should be able to only modify its modifier state without dispatching key events r=smaug, sr=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 19 Feb 2015 15:50:19 +0900
changeset 258621 cd6067b851b44adac4be50f0b00d2ea07b9ad5fc
parent 258620 87ed00d8ae5cfc0dacd1264f644834d8e61cede8
child 258622 6f20df12d0ff91f8a151ec55f9757268924fdfe7
push id721
push userjlund@mozilla.com
push dateTue, 21 Apr 2015 23:03:33 +0000
treeherdermozilla-release@d27c9211ebb3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, smaug
bugs1119609
milestone38.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 1119609 part.8 nsITextInputProcessor.keydown() and nsITextInputProcessor.keyup() should be able to only modify its modifier state without dispatching key events r=smaug, sr=smaug
dom/base/TextInputProcessor.cpp
dom/base/test/chrome/window_nsITextInputProcessor.xul
dom/interfaces/base/nsITextInputProcessor.idl
--- a/dom/base/TextInputProcessor.cpp
+++ b/dom/base/TextInputProcessor.cpp
@@ -475,16 +475,17 @@ TextInputProcessor::PrepareKeyboardEvent
              aKeyboardEvent.mKeyNameIndex < KEY_NAME_INDEX_USE_STRING) {
     // If KeyboardEvent.keyCode is 0, it may be uninitialized.  If so, we may
     // be able to decide a good .keyCode value if the .key value is a
     // non-printable key.
     aKeyboardEvent.keyCode =
       WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(
         aKeyboardEvent.mKeyNameIndex);
   }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextInputProcessor::Keydown(nsIDOMKeyEvent* aDOMKeyEvent,
                             uint32_t aKeyFlags,
                             uint8_t aOptionalArgc,
                             bool* aDoDefault)
@@ -518,16 +519,21 @@ TextInputProcessor::Keydown(nsIDOMKeyEve
       // If the modifier key is lockable modifier key such as CapsLock,
       // let's toggle modifier key state at keydown.
       ToggleModifierKey(modifierKeyData);
     } else {
       // Activate modifier flag before dispatching keydown event (i.e., keydown
       // event should indicate the releasing modifier is active.
       ActivateModifierKey(modifierKeyData);
     }
+    if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
+      return NS_OK;
+    }
+  } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
+    return NS_ERROR_INVALID_ARG;
   }
   keyEvent.modifiers = GetActiveModifiers();
 
   nsRefPtr<TextEventDispatcher> kungfuDeathGrip(mDispatcher);
   rv = IsValidStateForComposition();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -570,21 +576,27 @@ TextInputProcessor::Keyup(nsIDOMKeyEvent
   // We shouldn't modify the internal WidgetKeyboardEvent.
   WidgetKeyboardEvent keyEvent(*originalKeyEvent);
   nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   *aDoDefault = !(aKeyFlags & KEY_DEFAULT_PREVENTED);
 
-  if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex) &&
-      !WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
-    // Inactivate modifier flag before dispatching keyup event (i.e., keyup
-    // event shouldn't indicate the releasing modifier is active.
-    InactivateModifierKey(ModifierKeyData(keyEvent));
+  if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
+    if (!WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
+      // Inactivate modifier flag before dispatching keyup event (i.e., keyup
+      // event shouldn't indicate the releasing modifier is active.
+      InactivateModifierKey(ModifierKeyData(keyEvent));
+    }
+    if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
+      return NS_OK;
+    }
+  } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
+    return NS_ERROR_INVALID_ARG;
   }
   keyEvent.modifiers = GetActiveModifiers();
 
   nsRefPtr<TextEventDispatcher> kungfuDeathGrip(mDispatcher);
   rv = IsValidStateForComposition();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
--- a/dom/base/test/chrome/window_nsITextInputProcessor.xul
+++ b/dom/base/test/chrome/window_nsITextInputProcessor.xul
@@ -1250,16 +1250,35 @@ function runKeyTests()
       checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" keyup", [ ]);
       is(events.length, 5,
          description + testDesc + " should cause 5 events");
       checkModifiers(testDesc, events[0], "keydown",  kModifierKeys[i].key, kModifierKeys[i].code, [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[1], "keydown",  "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[2], "keypress", "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[3], "keyup",    "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[4], "keyup",    kModifierKeys[i].key, kModifierKeys[i].code, [ ]);
+
+      // KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT shouldn't cause key events of modifier keys, but should modify the modifier state.
+      reset();
+      doPreventDefaults = [ "keypress" ];
+      testDesc = "A modifier key \"" + kModifierKeys[i].key + "\" (\"" + kModifierKeys[i].code + "\") with KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT and a printable key";
+      TIP.keydown(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
+      TIP.keydown(keyA);
+      TIP.keyup(keyA);
+      TIP.keyup(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
+      TIP.keydown(keyA);
+      TIP.keyup(keyA);
+      is(events.length, 6,
+         description + testDesc + " should cause 6 events");
+      checkModifiers(testDesc, events[0], "keydown",  "a", "KeyA", [ kModifierKeys[i].key ]);
+      checkModifiers(testDesc, events[1], "keypress", "a", "KeyA", [ kModifierKeys[i].key ]);
+      checkModifiers(testDesc, events[2], "keyup",    "a", "KeyA", [ kModifierKeys[i].key ]);
+      checkModifiers(testDesc, events[3], "keydown",  "a", "KeyA", [ ]);
+      checkModifiers(testDesc, events[4], "keypress", "a", "KeyA", [ ]);
+      checkModifiers(testDesc, events[5], "keyup",    "a", "KeyA", [ ]);
     } else {
       TIP.keydown(modKey);
       checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" first keydown", [ kModifierKeys[i].key ]);
       TIP.keyup(modKey);
       checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" first keyup", [ kModifierKeys[i].key ]);
       TIP.keydown(keyA);
       checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ kModifierKeys[i].key ]);
       TIP.keyup(keyA);
@@ -1272,16 +1291,37 @@ function runKeyTests()
          description + testDesc + " should cause 7 events");
       checkModifiers(testDesc, events[0], "keydown",  kModifierKeys[i].key, kModifierKeys[i].code, [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[1], "keyup",    kModifierKeys[i].key, kModifierKeys[i].code, [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[2], "keydown",  "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[3], "keypress", "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[4], "keyup",    "a",                  "KeyA",                [ kModifierKeys[i].key ]);
       checkModifiers(testDesc, events[5], "keydown",  kModifierKeys[i].key, kModifierKeys[i].code, [ ]);
       checkModifiers(testDesc, events[6], "keyup",    kModifierKeys[i].key, kModifierKeys[i].code, [ ]);
+
+      // KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT shouldn't cause key events of modifier keys, but should modify the modifier state.
+      reset();
+      doPreventDefaults = [ "keypress" ];
+      testDesc = "A modifier key \"" + kModifierKeys[i].key + "\" (\"" + kModifierKeys[i].code + "\") with KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT and a printable key";
+      TIP.keydown(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
+      TIP.keyup(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
+      TIP.keydown(keyA);
+      TIP.keyup(keyA);
+      TIP.keydown(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
+      TIP.keyup(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
+      TIP.keydown(keyA);
+      TIP.keyup(keyA);
+      is(events.length, 6,
+         description + testDesc + " should cause 6 events");
+      checkModifiers(testDesc, events[0], "keydown",  "a",                  "KeyA",                [ kModifierKeys[i].key ]);
+      checkModifiers(testDesc, events[1], "keypress", "a",                  "KeyA",                [ kModifierKeys[i].key ]);
+      checkModifiers(testDesc, events[2], "keyup",    "a",                  "KeyA",                [ kModifierKeys[i].key ]);
+      checkModifiers(testDesc, events[3], "keydown",  "a",                  "KeyA",                [ ]);
+      checkModifiers(testDesc, events[4], "keypress", "a",                  "KeyA",                [ ]);
+      checkModifiers(testDesc, events[5], "keyup",    "a",                  "KeyA",                [ ]);
     }
   }
 
   // Modifier state should be inactivated only when all pressed modifiers are released
   var shiftLeft = new KeyboardEvent("", { key: "Shift", code: "ShiftLeft" });
   var shiftRight = new KeyboardEvent("", { key: "Shift", code: "ShiftRight" });
   var shiftVirtual = new KeyboardEvent("", { key: "Shift", code: "" });
   var altGrVirtual = new KeyboardEvent("", { key: "AltGraph", code: "" });
@@ -1843,16 +1883,36 @@ function runErrorTests()
     var keyEvent = new KeyboardEvent("", { key: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN });
     TIP.keyup(keyEvent, TIP.KEY_KEEP_KEYCODE_ZERO);
     ok(false,
        description + "keyup(KEY_KEEP_KEYCODE_ZERO) should fail if the .keyCode of the key event is initialized with non-zero value");
   } catch (e) {
     ok(e.message.contains("NS_ERROR_ILLEGAL_VALUE"),
        description + "keyup(KEY_KEEP_KEYCODE_ZERO) should cause NS_ERROR_ILLEGAL_VALUE if the .keyCode of the key event is initialized with nonzero value");
   }
+
+  // Specifying KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT with non-modifier key, it should cause an exception.
+  try {
+    var keyEvent = new KeyboardEvent("", { key: "a", code: "ShiftLeft" });
+    TIP.keyup(keyEvent, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
+    ok(false,
+       description + "keydown(KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) should fail if the .key value isn't a modifier key");
+  } catch (e) {
+    ok(e.message.contains("NS_ERROR_ILLEGAL_VALUE"),
+       description + "keydown(KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) should cause NS_ERROR_ILLEGAL_VALUE if the .key value isn't a modifier key");
+  }
+  try {
+    var keyEvent = new KeyboardEvent("", { key: "Enter", code: "ShiftLeft" });
+    TIP.keyup(keyEvent, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
+    ok(false,
+       description + "keydown(KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) should fail if the .key value isn't a modifier key");
+  } catch (e) {
+    ok(e.message.contains("NS_ERROR_ILLEGAL_VALUE"),
+       description + "keydown(KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) should cause NS_ERROR_ILLEGAL_VALUE if the .key value isn't a modifier key");
+  }
 }
 
 function runCommitCompositionTests()
 {
   var description = "runCommitCompositionTests(): ";
 
   var TIP = createTIP();
   ok(TIP.beginInputTransactionForTests(window),
--- a/dom/interfaces/base/nsITextInputProcessor.idl
+++ b/dom/interfaces/base/nsITextInputProcessor.idl
@@ -219,17 +219,17 @@ interface nsITextInputProcessorCallback;
  *
  *   // keyup of one of shift key doesn't cause inactivating "Shift" state.
  *   TIP.keyup(rightShift);
  *
  *   // This causes inactivating "Shift" state completely.
  *   TIP.keyup(leftShift);
  */
 
-[scriptable, builtinclass, uuid(c8b5d3fb-5bed-4b77-b67f-77aee6ac5081)]
+[scriptable, builtinclass, uuid(bb19b843-dbe2-41c5-8485-794121351d3a)]
 interface nsITextInputProcessor : nsISupports
 {
   /**
    * When you create an instance, you must call beginInputTransaction() first
    * except when you created the instance for automated tests.
    *
    * @param aWindow         A DOM window.  The instance will look for a top
    *                        level widget from this.
@@ -404,16 +404,21 @@ interface nsITextInputProcessor : nsISup
   // this flag causes throwing an exception.
   // NOTE: This is not recommended to use except for tests.
   const unsigned long KEY_KEEP_KEY_LOCATION_STANDARD               = 0x00000008;
   // If KEY_KEEP_KEYCODE_ZERO is specified when its .keyCode is not initialized
   // or initialized with 0, the value isn't computed with .key value when it
   // represents non-printable key.  Note that if .keyCode is initialized with
   // non-zero value, this flag causes throwing an exception.
   const unsigned long KEY_KEEP_KEYCODE_ZERO                        = 0x00000010;
+  // If KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT is specified when the key event is
+  // a modifier key's, keydown() and keyup() only modifies its modifier state
+  // without dispatching key events.  This is useful for testing odd behavior
+  // or emulating legacy API behavior.
+  const unsigned long KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT         = 0x00000020;
 
   /**
    * keydown() may dispatch a keydown event and some keypress events if
    * preceding keydown event isn't consumed and they are necessary.
    * Note that even if this is called during composition, key events may not
    * be dispatched.  In this case, this returns false.
    *
    * You should initialize at least .key value and .code value of the event.