Bug 751749 part.3 Editor should handle Win key as a modifier key r=ehsan
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 19 Jul 2012 10:28:17 +0900
changeset 102854 ae94d3a4497e5962de06dd58ded2b8376beb9765
parent 102853 372c0dbbfb5b6b305579ee85b442683aa6046d6f
child 102855 ea6903f1a7dff5ea87fb3a5c71ec1de72e353c17
push idunknown
push userunknown
push dateunknown
reviewersehsan
bugs751749
milestone17.0a1
Bug 751749 part.3 Editor should handle Win key as a modifier key r=ehsan
editor/libeditor/base/nsEditor.cpp
editor/libeditor/html/nsHTMLEditor.cpp
editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
editor/libeditor/text/nsPlaintextEditor.cpp
editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -5000,35 +5000,37 @@ nsEditor::HandleKeyPressEvent(nsIDOMKeyE
     if (nativeKeyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE) {
       aKeyEvent->PreventDefault();
     }
     return NS_OK;
   }
 
   switch (nativeKeyEvent->keyCode) {
     case nsIDOMKeyEvent::DOM_VK_META:
+    case nsIDOMKeyEvent::DOM_VK_WIN:
     case nsIDOMKeyEvent::DOM_VK_SHIFT:
     case nsIDOMKeyEvent::DOM_VK_CONTROL:
     case nsIDOMKeyEvent::DOM_VK_ALT:
       aKeyEvent->PreventDefault(); // consumed
       return NS_OK;
     case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
       if (nativeKeyEvent->IsControl() || nativeKeyEvent->IsAlt() ||
-          nativeKeyEvent->IsMeta()) {
+          nativeKeyEvent->IsMeta() || nativeKeyEvent->IsOS()) {
         return NS_OK;
       }
       DeleteSelection(nsIEditor::ePrevious, nsIEditor::eStrip);
       aKeyEvent->PreventDefault(); // consumed
       return NS_OK;
     case nsIDOMKeyEvent::DOM_VK_DELETE:
       // on certain platforms (such as windows) the shift key
       // modifies what delete does (cmd_cut in this case).
       // bailing here to allow the keybindings to do the cut.
       if (nativeKeyEvent->IsShift() || nativeKeyEvent->IsControl() ||
-          nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta()) {
+          nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
+          nativeKeyEvent->IsOS()) {
         return NS_OK;
       }
       DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
       aKeyEvent->PreventDefault(); // consumed
       return NS_OK; 
   }
   return NS_OK;
 }
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -594,16 +594,17 @@ nsHTMLEditor::HandleKeyPressEvent(nsIDOM
 
   nsKeyEvent* nativeKeyEvent = GetNativeKeyEvent(aKeyEvent);
   NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
   NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
                "HandleKeyPressEvent gets non-keypress event");
 
   switch (nativeKeyEvent->keyCode) {
     case nsIDOMKeyEvent::DOM_VK_META:
+    case nsIDOMKeyEvent::DOM_VK_WIN:
     case nsIDOMKeyEvent::DOM_VK_SHIFT:
     case nsIDOMKeyEvent::DOM_VK_CONTROL:
     case nsIDOMKeyEvent::DOM_VK_ALT:
     case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
     case nsIDOMKeyEvent::DOM_VK_DELETE:
       // These keys are handled on nsEditor, so, we can bypass
       // nsPlaintextEditor.
       return nsEditor::HandleKeyPressEvent(aKeyEvent);
@@ -614,17 +615,17 @@ nsHTMLEditor::HandleKeyPressEvent(nsIDOM
         return nsPlaintextEditor::HandleKeyPressEvent(aKeyEvent);
       }
 
       if (IsTabbable()) {
         return NS_OK; // let it be used for focus switching
       }
 
       if (nativeKeyEvent->IsControl() || nativeKeyEvent->IsAlt() ||
-          nativeKeyEvent->IsMeta()) {
+          nativeKeyEvent->IsMeta() || nativeKeyEvent->IsOS()) {
         return NS_OK;
       }
 
       nsCOMPtr<nsISelection> selection;
       nsresult rv = GetSelection(getter_AddRefs(selection));
       NS_ENSURE_SUCCESS(rv, rv);
       PRInt32 offset;
       nsCOMPtr<nsIDOMNode> node, blockParent;
@@ -664,32 +665,33 @@ nsHTMLEditor::HandleKeyPressEvent(nsIDOM
         return NS_OK; // don't type text for shift tabs
       }
       aKeyEvent->PreventDefault();
       return TypedText(NS_LITERAL_STRING("\t"), eTypedText);
     }
     case nsIDOMKeyEvent::DOM_VK_RETURN:
     case nsIDOMKeyEvent::DOM_VK_ENTER:
       if (nativeKeyEvent->IsControl() || nativeKeyEvent->IsAlt() ||
-          nativeKeyEvent->IsMeta()) {
+          nativeKeyEvent->IsMeta() || nativeKeyEvent->IsOS()) {
         return NS_OK;
       }
       aKeyEvent->PreventDefault(); // consumed
       if (nativeKeyEvent->IsShift() && !IsPlaintextEditor()) {
         // only inserts a br node
         return TypedText(EmptyString(), eTypedBR);
       }
       // uses rules to figure out what to insert
       return TypedText(EmptyString(), eTypedBreak);
   }
 
   // NOTE: On some keyboard layout, some characters are inputted with Control
   // key or Alt key, but at that time, widget sets FALSE to these keys.
   if (nativeKeyEvent->charCode == 0 || nativeKeyEvent->IsControl() ||
-      nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta()) {
+      nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
+      nativeKeyEvent->IsOS()) {
     // we don't PreventDefault() here or keybindings like control-x won't work
     return NS_OK;
   }
   aKeyEvent->PreventDefault();
   nsAutoString str(nativeKeyEvent->charCode);
   return TypedText(str, eTypedText);
 }
 
--- a/editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
+++ b/editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
@@ -132,16 +132,20 @@ function runTests()
     //   Basically, modifier keys shouldn't cause keypress event.  However,
     //   even if it were dispatched by widget's bug, editor should consume
     //   it when editor is editable.
     reset("");
     synthesizeKey("VK_META", { type: "keypress" });
     check(aDescription + "Meta", true, true, !aIsReadonly);
 
     reset("");
+    synthesizeKey("VK_WIN", { type: "keypress" });
+    check(aDescription + "OS", true, true, !aIsReadonly);
+
+    reset("");
     synthesizeKey("VK_SHIFT", { type: "keypress" });
     check(aDescription + "Shift", true, true, !aIsReadonly);
 
     reset("");
     synthesizeKey("VK_CONTROL", { type: "keypress" });
     check(aDescription + "Control", true, true, !aIsReadonly);
 
     // Alt key press event installs menubar key event listener, so,
@@ -171,16 +175,20 @@ function runTests()
     reset("");
     synthesizeKey("VK_BACK_SPACE", { altKey: true });
     check(aDescription + "Alt+Backspace", true, true, aIsReadonly);
 
     reset("");
     synthesizeKey("VK_BACK_SPACE", { metaKey: true });
     check(aDescription + "Meta+Backspace", true, true, aIsReadonly);
 
+    reset("");
+    synthesizeKey("VK_BACK_SPACE", { osKey: true });
+    check(aDescription + "OS+Backspace", true, true, aIsReadonly);
+
     // Delete key:
     //   If editor is readonly, it doesn't consume.
     //   If editor is editable, delete is consumed.
     //   Otherwise, editor doesn't consume the event.
     reset("");
     synthesizeKey("VK_DELETE", { });
     check(aDescription + "Delete", true, true, !aIsReadonly);
 
@@ -195,16 +203,20 @@ function runTests()
     reset("");
     synthesizeKey("VK_DELETE", { altKey: true });
     check(aDescription + "Alt+Delete", true, true, false);
 
     reset("");
     synthesizeKey("VK_DELETE", { metaKey: true });
     check(aDescription + "Meta+Delete", true, true, false);
 
+    reset("");
+    synthesizeKey("VK_DELETE", { osKey: true });
+    check(aDescription + "OS+Delete", true, true, false);
+
     // Return key:
     //   If editor is readonly, it doesn't consume.
     //   If editor is editable and not single line editor, it consumes Return
     //   and Shift+Return.
     //   Otherwise, editor doesn't consume the event.
     reset("a");
     synthesizeKey("VK_RETURN", { });
     check(aDescription + "Return",
@@ -229,16 +241,21 @@ function runTests()
     check(aDescription + "Alt+Return", true, true, false);
     is(aElement.innerHTML, "a", aDescription + "Alt+Return");
 
     reset("a");
     synthesizeKey("VK_RETURN", { metaKey: true });
     check(aDescription + "Meta+Return", true, true, false);
     is(aElement.innerHTML, "a", aDescription + "Meta+Return");
 
+    reset("a");
+    synthesizeKey("VK_RETURN", { osKey: true });
+    check(aDescription + "OS+Return", true, true, false);
+    is(aElement.innerHTML, "a", aDescription + "OS+Return");
+
     // Enter key (same as Return key):
     //   If editor is readonly, it doesn't consume.
     //   If editor is editable and not single line editor, it consumes Return
     //   and Shift+Return.
     //   Otherwise, editor doesn't consume the event.
     reset("a");
     synthesizeKey("VK_ENTER", { });
     check(aDescription + "Enter",
@@ -263,16 +280,21 @@ function runTests()
     check(aDescription + "Alt+Enter", true, true, false);
     is(aElement.innerHTML, "a", aDescription + "Alt+Enter");
 
     reset("a");
     synthesizeKey("VK_ENTER", { metaKey: true });
     check(aDescription + "Meta+Enter", true, true, false);
     is(aElement.innerHTML, "a", aDescription + "Meta+Enter");
 
+    reset("a");
+    synthesizeKey("VK_ENTER", { osKey: true });
+    check(aDescription + "OS+Enter", true, true, false);
+    is(aElement.innerHTML, "a", aDescription + "OS+Enter");
+
     // Tab key:
     //   If editor is tabbable, editor doesn't consume all tab key events.
     //   Otherwise, editor consumes tab key event without any modifier keys.
     reset("a");
     synthesizeKey("VK_TAB", { });
     check(aDescription + "Tab",
           true, true, !aIsTabbable && !aIsReadonly);
     is(aElement.innerHTML,
@@ -306,16 +328,23 @@ function runTests()
 
     reset("a");
     synthesizeKey("VK_TAB", { metaKey: true });
     check(aDescription + "Meta+Tab", true, true, false);
     is(aElement.innerHTML, "a", aDescription + "Meta+Tab");
     is(fm.focusedElement, aElement,
        aDescription + "focus moved unexpectedly (Meta+Tab)");
 
+    reset("a");
+    synthesizeKey("VK_TAB", { osKey: true });
+    check(aDescription + "OS+Tab", true, true, false);
+    is(aElement.innerHTML, "a", aDescription + "OS+Tab");
+    is(fm.focusedElement, aElement,
+       aDescription + "focus moved unexpectedly (OS+Tab)");
+
     // Indent/Outdent tests:
     // UL
     resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
     synthesizeKey("VK_TAB", { });
     check(aDescription + "Tab on UL",
           true, true, !aIsTabbable && !aIsReadonly);
     is(aElement.innerHTML,
        aIsReadonly || aIsTabbable ?
@@ -367,16 +396,24 @@ function runTests()
     resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
     synthesizeKey("VK_TAB", { metaKey: true });
     check(aDescription + "Meta+Tab on UL", true, true, false);
     is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>",
        aDescription + "Meta+Tab on UL");
     is(fm.focusedElement, aElement,
        aDescription + "focus moved unexpectedly (Meta+Tab on UL)");
 
+    resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
+    synthesizeKey("VK_TAB", { osKey: true });
+    check(aDescription + "OS+Tab on UL", true, true, false);
+    is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>",
+       aDescription + "OS+Tab on UL");
+    is(fm.focusedElement, aElement,
+       aDescription + "focus moved unexpectedly (OS+Tab on UL)");
+
     // OL
     resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
     synthesizeKey("VK_TAB", { });
     check(aDescription + "Tab on OL",
           true, true, !aIsTabbable && !aIsReadonly);
     is(aElement.innerHTML,
        aIsReadonly || aIsTabbable ?
          "<ol><li id=\"target\">ol list item</li></ol>" :
@@ -427,16 +464,24 @@ function runTests()
     resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
     synthesizeKey("VK_TAB", { metaKey: true });
     check(aDescription + "Meta+Tab on OL", true, true, false);
     is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>",
        aDescription + "Meta+Tab on OL");
     is(fm.focusedElement, aElement,
        aDescription + "focus moved unexpectedly (Meta+Tab on OL)");
 
+    resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
+    synthesizeKey("VK_TAB", { osKey: true });
+    check(aDescription + "OS+Tab on OL", true, true, false);
+    is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>",
+       aDescription + "OS+Tab on OL");
+    is(fm.focusedElement, aElement,
+       aDescription + "focus moved unexpectedly (OS+Tab on OL)");
+
     // TD
     resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
     synthesizeKey("VK_TAB", { });
     check(aDescription + "Tab on TD",
           true, true, !aIsTabbable && !aIsReadonly);
     is(aElement.innerHTML,
        aIsTabbable || aIsReadonly ?
          "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>" :
@@ -489,16 +534,25 @@ function runTests()
     synthesizeKey("VK_TAB", { metaKey: true });
     check(aDescription + "Meta+Tab on TD", true, true, false);
     is(aElement.innerHTML,
        "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
        aDescription + "Meta+Tab on TD");
     is(fm.focusedElement, aElement,
        aDescription + "focus moved unexpectedly (Meta+Tab on TD)");
 
+    resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
+    synthesizeKey("VK_TAB", { osKey: true });
+    check(aDescription + "OS+Tab on TD", true, true, false);
+    is(aElement.innerHTML,
+       "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
+       aDescription + "OS+Tab on TD");
+    is(fm.focusedElement, aElement,
+       aDescription + "focus moved unexpectedly (OS+Tab on TD)");
+
     // TH
     resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
     synthesizeKey("VK_TAB", { });
     check(aDescription + "Tab on TH",
           true, true, !aIsTabbable && !aIsReadonly);
     is(aElement.innerHTML,
        aIsTabbable || aIsReadonly ?
          "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>" :
@@ -551,16 +605,25 @@ function runTests()
     synthesizeKey("VK_TAB", { metaKey: true });
     check(aDescription + "Meta+Tab on TH", true, true, false);
     is(aElement.innerHTML,
        "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
        aDescription + "Meta+Tab on TH");
     is(fm.focusedElement, aElement,
        aDescription + "focus moved unexpectedly (Meta+Tab on TH)");
 
+    resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
+    synthesizeKey("VK_TAB", { osKey: true });
+    check(aDescription + "OS+Tab on TH", true, true, false);
+    is(aElement.innerHTML,
+       "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
+       aDescription + "OS+Tab on TH");
+    is(fm.focusedElement, aElement,
+       aDescription + "focus moved unexpectedly (OS+Tab on TH)");
+
     // Esc key:
     //   In all cases, esc key events are not consumed
     reset("abc");
     synthesizeKey("VK_ESCAPE", { });
     check(aDescription + "Esc", true, true, false);
 
     reset("abc");
     synthesizeKey("VK_ESCAPE", { shiftKey: true });
@@ -573,16 +636,20 @@ function runTests()
     reset("abc");
     synthesizeKey("VK_ESCAPE", { altKey: true });
     check(aDescription + "Alt+Esc", true, true, false);
 
     reset("abc");
     synthesizeKey("VK_ESCAPE", { metaKey: true });
     check(aDescription + "Meta+Esc", true, true, false);
 
+    reset("abc");
+    synthesizeKey("VK_ESCAPE", { osKey: true });
+    check(aDescription + "OS+Esc", true, true, false);
+
     // typical typing tests:
     reset("");
     synthesizeKey("M", { shiftKey: true });
     check(aDescription + "M", true, true, !aIsReadonly);
     synthesizeKey("o", { });
     check(aDescription + "o", true, true, !aIsReadonly);
     synthesizeKey("z", { });
     check(aDescription + "z", true, true, !aIsReadonly);
--- a/editor/libeditor/text/nsPlaintextEditor.cpp
+++ b/editor/libeditor/text/nsPlaintextEditor.cpp
@@ -353,51 +353,55 @@ nsPlaintextEditor::HandleKeyPressEvent(n
 
   nsKeyEvent* nativeKeyEvent = GetNativeKeyEvent(aKeyEvent);
   NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
   NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
                "HandleKeyPressEvent gets non-keypress event");
 
   switch (nativeKeyEvent->keyCode) {
     case nsIDOMKeyEvent::DOM_VK_META:
+    case nsIDOMKeyEvent::DOM_VK_WIN:
     case nsIDOMKeyEvent::DOM_VK_SHIFT:
     case nsIDOMKeyEvent::DOM_VK_CONTROL:
     case nsIDOMKeyEvent::DOM_VK_ALT:
     case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
     case nsIDOMKeyEvent::DOM_VK_DELETE:
       // These keys are handled on nsEditor
       return nsEditor::HandleKeyPressEvent(aKeyEvent);
     case nsIDOMKeyEvent::DOM_VK_TAB: {
       if (IsTabbable()) {
         return NS_OK; // let it be used for focus switching
       }
 
       if (nativeKeyEvent->IsShift() || nativeKeyEvent->IsControl() ||
-          nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta()) {
+          nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
+          nativeKeyEvent->IsOS()) {
         return NS_OK;
       }
 
       // else we insert the tab straight through
       aKeyEvent->PreventDefault();
       return TypedText(NS_LITERAL_STRING("\t"), eTypedText);
     }
     case nsIDOMKeyEvent::DOM_VK_RETURN:
     case nsIDOMKeyEvent::DOM_VK_ENTER:
       if (IsSingleLineEditor() || nativeKeyEvent->IsControl() ||
-          nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta()) {
+          nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
+          nativeKeyEvent->IsOS()) {
         return NS_OK;
       }
       aKeyEvent->PreventDefault();
       return TypedText(EmptyString(), eTypedBreak);
   }
 
   // NOTE: On some keyboard layout, some characters are inputted with Control
   // key or Alt key, but at that time, widget sets FALSE to these keys.
   if (nativeKeyEvent->charCode == 0 || nativeKeyEvent->IsControl() ||
-      nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta()) {
+      nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
+      nativeKeyEvent->IsOS()) {
     // we don't PreventDefault() here or keybindings like control-x won't work
     return NS_OK;
   }
   aKeyEvent->PreventDefault();
   nsAutoString str(nativeKeyEvent->charCode);
   return TypedText(str, eTypedText);
 }
 
--- a/editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
+++ b/editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
@@ -119,16 +119,20 @@ function runTests()
     //   Basically, modifier keys shouldn't cause keypress event.  However,
     //   even if it were dispatched by widget's bug, editor should consume
     //   it when editor is editable.
     reset("");
     synthesizeKey("VK_META", { type: "keypress" });
     check(aDescription + "Meta", true, true, !aIsReadonly);
 
     reset("");
+    synthesizeKey("VK_WIN", { type: "keypress" });
+    check(aDescription + "OS", true, true, !aIsReadonly);
+
+    reset("");
     synthesizeKey("VK_SHIFT", { type: "keypress" });
     check(aDescription + "Shift", true, true, !aIsReadonly);
 
     reset("");
     synthesizeKey("VK_CONTROL", { type: "keypress" });
     check(aDescription + "Control", true, true, !aIsReadonly);
 
     // Alt key press event installs menubar key event listener, so,
@@ -164,16 +168,20 @@ function runTests()
     // Mac: cmd_deleteWordBackward
     check(aDescription + "Alt+Backspace",
           true, true, aIsReadonly || kIsWin || kIsMac);
 
     reset("");
     synthesizeKey("VK_BACK_SPACE", { metaKey: true });
     check(aDescription + "Meta+Backspace", true, true, aIsReadonly);
 
+    reset("");
+    synthesizeKey("VK_BACK_SPACE", { osKey: true });
+    check(aDescription + "OS+Backspace", true, true, aIsReadonly);
+
     // Delete key:
     //   If editor is readonly, it doesn't consume.
     //   If editor is editable, delete is consumed.
     //   Otherwise, editor doesn't consume the event but the native key
     //   bindings on nsTextControlFrame may consume it.
     reset("");
     synthesizeKey("VK_DELETE", { });
     // Linux: native handler
@@ -201,16 +209,21 @@ function runTests()
           true, true, kIsMac);
 
     reset("");
     synthesizeKey("VK_DELETE", { metaKey: true });
     // Linux: native handler consumed.
     check(aDescription + "Meta+Delete",
           true, true, kIsLinux);
 
+    reset("");
+    synthesizeKey("VK_DELETE", { osKey: true });
+    check(aDescription + "OS+Delete",
+          true, true, false);
+
     // XXX input.value returns "\n" when it's empty, so, we should use dummy
     // value ("a") for the following tests.
 
     // Return key:
     //   If editor is readonly, it doesn't consume.
     //   If editor is editable and not single line editor, it consumes Return
     //   and Shift+Return.
     //   Otherwise, editor doesn't consume the event.
@@ -238,16 +251,21 @@ function runTests()
     check(aDescription + "Alt+Return", true, true, false);
     is(aElement.value, "a", aDescription + "Alt+Return");
 
     reset("a");
     synthesizeKey("VK_RETURN", { metaKey: true });
     check(aDescription + "Meta+Return", true, true, false);
     is(aElement.value, "a", aDescription + "Meta+Return");
 
+    reset("a");
+    synthesizeKey("VK_RETURN", { osKey: true });
+    check(aDescription + "OS+Return", true, true, false);
+    is(aElement.value, "a", aDescription + "OS+Return");
+
     // Enter key (same as Return key):
     //   If editor is readonly, it doesn't consume.
     //   If editor is editable and not single line editor, it consumes Return
     //   and Shift+Return.
     //   Otherwise, editor doesn't consume the event.
     reset("a");
     synthesizeKey("VK_ENTER", { });
     check(aDescription + "Enter",
@@ -272,16 +290,21 @@ function runTests()
     check(aDescription + "Alt+Enter", true, true, false);
     is(aElement.value, "a", aDescription + "Alt+Enter");
 
     reset("a");
     synthesizeKey("VK_ENTER", { metaKey: true });
     check(aDescription + "Meta+Enter", true, true, false);
     is(aElement.value, "a", aDescription + "Meta+Enter");
 
+    reset("a");
+    synthesizeKey("VK_ENTER", { osKey: true });
+    check(aDescription + "OS+Enter", true, true, false);
+    is(aElement.value, "a", aDescription + "OS+Enter");
+
     // Tab key:
     //   If editor is tabbable, editor doesn't consume all tab key events.
     //   Otherwise, editor consumes tab key event without any modifier keys.
     reset("a");
     synthesizeKey("VK_TAB", { });
     check(aDescription + "Tab",
           true, true, !aIsTabbable && !aIsReadonly);
     is(aElement.value, !aIsTabbable && !aIsReadonly ? "a\t" : "a",
@@ -325,16 +348,23 @@ function runTests()
 
     reset("a");
     synthesizeKey("VK_TAB", { metaKey: true });
     check(aDescription + "Meta+Tab", true, true, false);
     is(aElement.value, "a", aDescription + "Meta+Tab");
     is(fm.focusedElement, aElement,
        aDescription + "focus moved unexpectedly (Meta+Tab)");
 
+    reset("a");
+    synthesizeKey("VK_TAB", { osKey: true });
+    check(aDescription + "OS+Tab", true, true, false);
+    is(aElement.value, "a", aDescription + "OS+Tab");
+    is(fm.focusedElement, aElement,
+       aDescription + "focus moved unexpectedly (OS+Tab)");
+
     // Esc key:
     //   In all cases, esc key events are not consumed
     reset("abc");
     synthesizeKey("VK_ESCAPE", { });
     check(aDescription + "Esc", true, true, false);
 
     reset("abc");
     synthesizeKey("VK_ESCAPE", { shiftKey: true });
@@ -347,16 +377,20 @@ function runTests()
     reset("abc");
     synthesizeKey("VK_ESCAPE", { altKey: true });
     check(aDescription + "Alt+Esc", true, true, false);
 
     reset("abc");
     synthesizeKey("VK_ESCAPE", { metaKey: true });
     check(aDescription + "Meta+Esc", true, true, false);
 
+    reset("abc");
+    synthesizeKey("VK_ESCAPE", { osKey: true });
+    check(aDescription + "OS+Esc", true, true, false);
+
     // typical typing tests:
     reset("");
     synthesizeKey("M", { shiftKey: true });
     check(aDescription + "M", true, true, !aIsReadonly);
     synthesizeKey("o", { });
     check(aDescription + "o", true, true, !aIsReadonly);
     synthesizeKey("z", { });
     check(aDescription + "z", true, true, !aIsReadonly);