Bug 1387828 - Reinstate support for initKeyboardEvent; r?smaug draft
authorAryeh Gregor <ayg@aryeh.name>
Sun, 06 Aug 2017 16:52:39 +0300
changeset 645597 9bb57aac895d4c62400f08832730fc6883ab4950
parent 645321 1f363abd841d1ad9fb8ac1e67ea7c8636895d766
child 725934 b57483a2666b16868ef41950e362a171bb11b557
push id73793
push userbmo:ayg@aryeh.name
push dateSun, 13 Aug 2017 14:40:20 +0000
reviewerssmaug
bugs1387828
milestone57.0a1
Bug 1387828 - Reinstate support for initKeyboardEvent; r?smaug https://github.com/w3c/uievents/issues/112 This is supported by all other UAs. In the past we had compatibility problems when trying to add support, but it seems these might be fixed if we make all arguments optional beyond the first. The interface chosen for the method is from the spec, which has been updated to match Chrome. This is also very similar to WebKit, but the final four arguments are different from IE. MozReview-Commit-ID: 36AeX1JwJTt
dom/events/KeyboardEvent.cpp
dom/events/KeyboardEvent.h
dom/events/test/test_dom_keyboard_event.html
dom/webidl/KeyboardEvent.webidl
testing/web-platform/meta/dom/events/Event-init-while-dispatching.html.ini
--- a/dom/events/KeyboardEvent.cpp
+++ b/dom/events/KeyboardEvent.cpp
@@ -336,16 +336,40 @@ KeyboardEvent::InitKeyEvent(const nsAStr
   WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent();
   keyEvent->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey);
   keyEvent->mKeyCode = aKeyCode;
   keyEvent->mCharCode = aCharCode;
 
   return NS_OK;
 }
 
+void
+KeyboardEvent::InitKeyboardEvent(const nsAString& aType,
+                                 bool aCanBubble,
+                                 bool aCancelable,
+                                 nsGlobalWindow* aView,
+                                 const nsAString& aKey,
+                                 uint32_t aLocation,
+                                 bool aCtrlKey,
+                                 bool aAltKey,
+                                 bool aShiftKey,
+                                 bool aMetaKey,
+                                 ErrorResult& aRv)
+{
+  NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
+
+  UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, 0);
+
+  WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent();
+  keyEvent->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey);
+  keyEvent->mLocation = aLocation;
+  keyEvent->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
+  keyEvent->mKeyValue = aKey;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 already_AddRefed<KeyboardEvent>
 NS_NewDOMKeyboardEvent(EventTarget* aOwner,
--- a/dom/events/KeyboardEvent.h
+++ b/dom/events/KeyboardEvent.h
@@ -67,16 +67,22 @@ public:
                     bool aShiftKey, bool aMetaKey,
                     uint32_t aKeyCode, uint32_t aCharCode)
   {
     auto* view = aView ? aView->AsInner() : nullptr;
     InitKeyEvent(aType, aCanBubble, aCancelable, view, aCtrlKey, aAltKey,
                  aShiftKey, aMetaKey, aKeyCode, aCharCode);
   }
 
+  void InitKeyboardEvent(const nsAString& aType,
+                         bool aCanBubble, bool aCancelable,
+                         nsGlobalWindow* aView, const nsAString& aKey,
+                         uint32_t aLocation, bool aCtrlKey, bool aAltKey,
+                         bool aShiftKey, bool aMetaKey, ErrorResult& aRv);
+
 protected:
   ~KeyboardEvent() {}
 
   void InitWithKeyboardEventInit(EventTarget* aOwner,
                                  const nsAString& aType,
                                  const KeyboardEventInit& aParam,
                                  ErrorResult& aRv);
 
--- a/dom/events/test/test_dom_keyboard_event.html
+++ b/dom/events/test/test_dom_keyboard_event.html
@@ -15,100 +15,161 @@
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(runTests, window);
 
 function testInitializingUntrustedEvent()
 {
   const kTests = [
-    { createEventArg: "KeyboardEvent",
+    // initKeyEvent
+    { createEventArg: "KeyboardEvent", useInitKeyboardEvent: false,
       type: "keydown", bubbles: true, cancelable: true, view: null,
       ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
-      keyCode: 0x00, charCode: 0x00 },
+      keyCode: 0x00, charCode: 0x00, detail: 0, key: "", location: 0,
+    }, // 0
 
-    { createEventArg: "keyboardevent",
+    { createEventArg: "keyboardevent", useInitKeyboardEvent: false,
       type: "keyup", bubbles: false, cancelable: true, view: window,
       ctrlKey: true, altKey: false, shiftKey: false, metaKey: false,
-      keyCode: 0x10, charCode: 0x00 },
+      keyCode: 0x10, charCode: 0x00, detail: 0, key: "", location: 0,
+    }, // 1
 
-    { createEventArg: "Keyboardevent",
+    { createEventArg: "Keyboardevent", useInitKeyboardEvent: false,
       type: "keypess", bubbles: true, cancelable: false, view: null,
       ctrlKey: false, altKey: true, shiftKey: false, metaKey: false,
-      keyCode: 0x11, charCode: 0x30 },
+      keyCode: 0x11, charCode: 0x30, detail: 0, key: "", location: 0,
+    }, // 2
 
-    { createEventArg: "keyboardEvent",
+    { createEventArg: "keyboardEvent", useInitKeyboardEvent: false,
       type: "boo", bubbles: false, cancelable: false, view: window,
       ctrlKey: false, altKey: false, shiftKey: true, metaKey: false,
-      keyCode: 0x30, charCode: 0x40 },
+      keyCode: 0x30, charCode: 0x40, detail: 0, key: "", location: 0,
+    }, // 3
 
-    { createEventArg: "KeyBoardEvent",
+    { createEventArg: "KeyBoardEvent", useInitKeyboardEvent: false,
       type: "foo", bubbles: true, cancelable: true, view: null,
       ctrlKey: false, altKey: false, shiftKey: false, metaKey: true,
-      keyCode: 0x00, charCode: 0x50 },
+      keyCode: 0x00, charCode: 0x50, detail: 0, key: "", location: 0,
+    }, // 4
 
-    { createEventArg: "keyboardevEnt",
+    { createEventArg: "keyboardevEnt", useInitKeyboardEvent: false,
       type: "bar", bubbles: false, cancelable: true, view: window,
       ctrlKey: true, altKey: true, shiftKey: false, metaKey: false,
-      keyCode: 0x00, charCode: 0x60 },
+      keyCode: 0x00, charCode: 0x60, detail: 0, key: "", location: 0,
+    }, // 5
 
-    { createEventArg: "KeyboaRdevent",
+    { createEventArg: "KeyboaRdevent", useInitKeyboardEvent: false,
       type: "keydown", bubbles: true, cancelable: false, view: null,
       ctrlKey: false, altKey: true, shiftKey: false, metaKey: true,
-      keyCode: 0x30, charCode: 0x00 },
+      keyCode: 0x30, charCode: 0x00, detail: 0, key: "", location: 0,
+    }, // 6
 
-    { createEventArg: "KEYBOARDEVENT",
+    { createEventArg: "KEYBOARDEVENT", useInitKeyboardEvent: false,
       type: "keyup", bubbles: false, cancelable: false, view: window,
       ctrlKey: true, altKey: false, shiftKey: true, metaKey: false,
-      keyCode: 0x10, charCode: 0x80 },
+      keyCode: 0x10, charCode: 0x80, detail: 0, key: "", location: 0,
+    }, // 7
 
-    { createEventArg: "KeyboardEvent",
+    { createEventArg: "KeyboardEvent", useInitKeyboardEvent: false,
       type: "keypress", bubbles: false, cancelable: false, view: window,
       ctrlKey: true, altKey: false, shiftKey: true, metaKey: true,
-      keyCode: 0x10, charCode: 0x80 },
+      keyCode: 0x10, charCode: 0x80, detail: 0, key: "", location: 0,
+    }, // 8
 
-    { createEventArg: "KeyboardEvent",
+    { createEventArg: "KeyboardEvent", useInitKeyboardEvent: false,
       type: "foo", bubbles: false, cancelable: false, view: window,
       ctrlKey: true, altKey: true, shiftKey: true, metaKey: true,
-      keyCode: 0x10, charCode: 0x80 },
+      keyCode: 0x10, charCode: 0x80, detail: 0, key: "", location: 0,
+    }, // 9
+
+    // initKeyboardEvent
+    { createEventArg: "KeyboardEvent", useInitKeyboardEvent: true,
+      type: "keydown", bubbles: true, cancelable: true, view: null,
+      ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
+      keyCode: 0x00, charCode: 0x00, key: "", location: 0,
+    }, // 10
+
+    { createEventArg: "keyboardevent", useInitKeyboardEvent: true,
+      type: "keyup", bubbles: false, cancelable: true, view: window,
+      ctrlKey: true, altKey: false, shiftKey: false, metaKey: false,
+      keyCode: 0x00, charCode: 0x00, key: "Unidentified", location: 1,
+    }, // 11
+
+    { createEventArg: "Keyboardevent", useInitKeyboardEvent: true,
+      type: "keypess", bubbles: true, cancelable: false, view: null,
+      ctrlKey: false, altKey: true, shiftKey: false, metaKey: false,
+      keyCode: 0x00, charCode: 0x00, key: "FooBar", location: 2,
+    }, // 12
+
+    { createEventArg: "keyboardevent", useInitKeyboardEvent: true,
+      type: "foo", bubbles: true, cancelable: true, view: null,
+      ctrlKey: false, altKey: false, shiftKey: false, metaKey: true,
+      keyCode: 0x00, charCode: 0x00, key: "a", location: 0,
+    }, // 13
+
+    { createEventArg: "KeyBoardEvent", useInitKeyboardEvent: true,
+      type: "", bubbles: false, cancelable: false, view: null,
+      ctrlKey: true, altKey: true, shiftKey: true, metaKey: true,
+      keyCode: 0x00, charCode: 0x00, key: "3", location: 0,
+    }, // 14
+
+    { createEventArg: "keyboardevEnt", useInitKeyboardEvent: true,
+      type: "", bubbles: false, cancelable: false, view: null,
+      ctrlKey: false, altKey: false, shiftKey: true, metaKey: false,
+      keyCode: 0x00, charCode: 0x00, key: "3", location: 6,
+    }, // 15
+
+    { createEventArg: "KeyboaRdevent", useInitKeyboardEvent: true,
+      type: "", bubbles: false, cancelable: false, view: null,
+      ctrlKey: false, altKey: true, shiftKey: false, metaKey: false,
+      keyCode: 0x00, charCode: 0x00, key: "", location: 4,
+    }, // 16
   ];
 
   const kOtherModifierName = [
     "CapsLock", "NumLock", "ScrollLock", "Symbol", "SymbolLock", "Fn", "FnLock", "OS", "AltGraph"
   ];
 
   const kInvalidModifierName = [
     "shift", "control", "alt", "meta", "capslock", "numlock", "scrolllock",
     "symbollock", "fn", "os", "altgraph", "Invalid", "Shift Control",
     "Win", "Scroll"
   ];
 
   for (var i = 0; i < kTests.length; i++) {
     var description = "testInitializingUntrustedEvent, Index: " + i + ", ";
     const kTest = kTests[i];
     var e = document.createEvent(kTest.createEventArg);
-    e.initKeyEvent(kTest.type, kTest.bubbles, kTest.cancelable, kTest.view,
-                   kTest.ctrlKey, kTest.altKey, kTest.shiftKey, kTest.metaKey,
-                   kTest.keyCode, kTest.charCode);
+    if (kTest.useInitKeyboardEvent) {
+      e.initKeyboardEvent(kTest.type, kTest.bubbles, kTest.cancelable,
+                          kTest.view, kTest.key, kTest.location,
+                          kTest.ctrlKey, kTest.altKey, kTest.shiftKey,
+                          kTest.metaKey);
+    } else {
+      e.initKeyEvent(kTest.type, kTest.bubbles, kTest.cancelable, kTest.view,
+                     kTest.ctrlKey, kTest.altKey, kTest.shiftKey, kTest.metaKey,
+                     kTest.keyCode, kTest.charCode);
+    }
     is(e.toString(), "[object KeyboardEvent]",
        description + 'class string should be "KeyboardEvent"');
 
     for (var attr in kTest) {
-      if (attr == "createEventArg") {
+      if (attr == "createEventArg" || attr == "useInitKeyboardEvent" || attr == "modifiersList") {
         continue;
       }
-      if (attr == "keyCode") {
+      if (!kTest.useInitKeyboardEvent && attr == "keyCode") {
         // If this is keydown, keyup of keypress event, keycod must be correct.
         if (kTest.type == "keydown" || kTest.type == "keyup" || kTest.type == "keypress") {
           is(e[attr], kTest[attr], description + attr + " returns wrong value");
         // Otherwise, should be always zero (why?)
         } else {
           is(e[attr], 0, description + attr + " returns non-zero for invalid event");
         }
-      } else if (attr == "charCode") {
+      } else if (!kTest.useInitKeyboardEvent && attr == "charCode") {
         // If this is keydown or keyup event, charCode always 0.
         if (kTest.type == "keydown" || kTest.type == "keyup") {
           is(e[attr], 0, description + attr + " returns non-zero for keydown or keyup event");
         // If this is keypress event, charCode must be correct.
         } else if (kTest.type == "keypress") {
           is(e[attr], kTest[attr], description + attr + " returns wrong value");
         // Otherwise, we have a bug.
         } else {
@@ -117,20 +178,16 @@ function testInitializingUntrustedEvent(
           }
         }
       } else {
         is(e[attr], kTest[attr], description + attr + " returns wrong value");
       }
     }
     is(e.isTrusted, false, description + "isTrusted returns wrong value");
 
-    // key and code values cannot be initialized with initKeyEvent().
-    is(e.key, "", description + "key must return empty string");
-    is(e.code, "", description + "code must be empty string");
-
     // getModifierState() tests
     is(e.getModifierState("Shift"), kTest.shiftKey,
        description + "getModifierState(\"Shift\") returns wrong value");
     is(e.getModifierState("Control"), kTest.ctrlKey,
        description + "getModifierState(\"Control\") returns wrong value");
     is(e.getModifierState("Alt"), kTest.altKey,
        description + "getModifierState(\"Alt\") returns wrong value");
     is(e.getModifierState("Meta"), kTest.metaKey,
--- a/dom/webidl/KeyboardEvent.webidl
+++ b/dom/webidl/KeyboardEvent.webidl
@@ -24,16 +24,28 @@ interface KeyboardEvent : UIEvent
 
   readonly attribute unsigned long location;
   readonly attribute boolean       repeat;
   readonly attribute boolean       isComposing;
 
   readonly attribute DOMString key;
   readonly attribute DOMString code;
 
+  [Throws]
+  void initKeyboardEvent(DOMString typeArg,
+                         optional boolean bubblesArg = false,
+                         optional boolean cancelableArg = false,
+                         optional Window? viewArg = null,
+                         optional DOMString keyArg = "",
+                         optional unsigned long locationArg = 0,
+                         optional boolean ctrlKey = false,
+                         optional boolean altKey = false,
+                         optional boolean shiftKey = false,
+                         optional boolean metaKey = false);
+
   // This returns the initialized dictionary for generating a
   // same-type keyboard event
   [Cached, ChromeOnly, Constant]
   readonly attribute KeyboardEventInit initDict;
 };
 
 dictionary KeyboardEventInit : EventModifierInit
 {
deleted file mode 100644
--- a/testing/web-platform/meta/dom/events/Event-init-while-dispatching.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[Event-init-while-dispatching.html]
-  type: testharness
-  [Calling initKeyboardEvent while dispatching.]
-    expected: FAIL
-    bug: https://github.com/w3c/web-platform-tests/pull/4117
-