Bug 1245153 - Make EventUtils.js use aWindow argument for sub-calls consistently; r=jmaher
authorAndreas Tolfsen <ato@mozilla.com>
Mon, 08 Feb 2016 13:59:40 +0000
changeset 321338 489672415fb3b8615f32170d680f9837094089db
parent 321337 234abb0664d43f72f1c60e1cc4884017cc1bc6e9
child 321339 32dc11f5f92e014d30c18d3a4045d5f8363694c2
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmaher
bugs1245153
milestone47.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 1245153 - Make EventUtils.js use aWindow argument for sub-calls consistently; r=jmaher EventUtils.js previously allowed you to override the Window object reference through passing it as an optional argument to its functions. This change fixes certain uses of implicit globals that reside on Window. MozReview-Commit-ID: EJT8iIs85ej
testing/mochitest/tests/SimpleTest/EventUtils.js
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -109,17 +109,17 @@ function sendMouseEvent(aEvent, aTarget,
 }
 
 /**
  * Send a drag event to the node aTarget (aTarget can be an id, or an
  * actual node) . The "event" passed in to aEvent is just a JavaScript
  * object with the properties set that the real drag event object should
  * have. This includes the type of the drag event.
  */
-function sendDragEvent(aEvent, aTarget, aWindow=window) {
+function sendDragEvent(aEvent, aTarget, aWindow = window) {
   if (['drag', 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave', 'drop'].indexOf(aEvent.type) == -1) {
     throw new Error("sendDragEvent doesn't know about event type '" + aEvent.type + "'");
   }
 
   if (typeof aTarget == "string") {
     aTarget = aWindow.document.getElementById(aTarget);
   }
 
@@ -214,19 +214,20 @@ function sendKey(aKey, aWindow) {
   var keyName = "VK_" + aKey.toUpperCase();
   synthesizeKey(keyName, { shiftKey: false }, aWindow);
 }
 
 /**
  * Parse the key modifier flags from aEvent. Used to share code between
  * synthesizeMouse and synthesizeKey.
  */
-function _parseModifiers(aEvent)
+function _parseModifiers(aEvent, aWindow = window)
 {
-  const nsIDOMWindowUtils = _EU_Ci.nsIDOMWindowUtils;
+  var navigator = _getNavigator(aWindow);
+  var nsIDOMWindowUtils = _EU_Ci.nsIDOMWindowUtils;
   var mval = 0;
   if (aEvent.shiftKey) {
     mval |= nsIDOMWindowUtils.MODIFIER_SHIFT;
   }
   if (aEvent.ctrlKey) {
     mval |= nsIDOMWindowUtils.MODIFIER_CONTROL;
   }
   if (aEvent.altKey) {
@@ -310,25 +311,25 @@ function synthesizePointer(aTarget, aOff
  * aEvent is an object which may contain the properties:
  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
  *
  * If the type is specified, an mouse event of that type is fired. Otherwise,
  * a mousedown followed by a mouse up is performed.
  *
  * aWindow is optional, and defaults to the current window object.
  */
-function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
+function synthesizeMouseAtPoint(left, top, aEvent, aWindow = window)
 {
   var utils = _getDOMWindowUtils(aWindow);
   var defaultPrevented = false;
 
   if (utils) {
     var button = computeButton(aEvent);
     var clickCount = aEvent.clickCount || 1;
-    var modifiers = _parseModifiers(aEvent);
+    var modifiers = _parseModifiers(aEvent, aWindow);
     var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0;
     var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0;
     var synthesized = ("isSynthesized" in aEvent) ? aEvent.isSynthesized : true;
 
     if (("type" in aEvent) && aEvent.type) {
       defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button,
                                               clickCount, modifiers, false,
                                               pressure, inputSource,
@@ -337,46 +338,48 @@ function synthesizeMouseAtPoint(left, to
     else {
       utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers, false, pressure, inputSource);
       utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers, false, pressure, inputSource);
     }
   }
 
   return defaultPrevented;
 }
-function synthesizeTouchAtPoint(left, top, aEvent, aWindow)
+
+function synthesizeTouchAtPoint(left, top, aEvent, aWindow = window)
 {
   var utils = _getDOMWindowUtils(aWindow);
 
   if (utils) {
     var id = aEvent.id || 0;
     var rx = aEvent.rx || 1;
     var ry = aEvent.rx || 1;
     var angle = aEvent.angle || 0;
     var force = aEvent.force || 1;
-    var modifiers = _parseModifiers(aEvent);
+    var modifiers = _parseModifiers(aEvent, aWindow);
 
     if (("type" in aEvent) && aEvent.type) {
       utils.sendTouchEvent(aEvent.type, [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
     }
     else {
       utils.sendTouchEvent("touchstart", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
       utils.sendTouchEvent("touchend", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
     }
   }
 }
-function synthesizePointerAtPoint(left, top, aEvent, aWindow)
+
+function synthesizePointerAtPoint(left, top, aEvent, aWindow = window)
 {
   var utils = _getDOMWindowUtils(aWindow);
   var defaultPrevented = false;
 
   if (utils) {
     var button = computeButton(aEvent);
     var clickCount = aEvent.clickCount || 1;
-    var modifiers = _parseModifiers(aEvent);
+    var modifiers = _parseModifiers(aEvent, aWindow);
     var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0;
     var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0;
     var synthesized = ("isSynthesized" in aEvent) ? aEvent.isSynthesized : true;
     var isPrimary = ("isPrimary" in aEvent) ? aEvent.isPrimary : false;
 
     if (("type" in aEvent) && aEvent.type) {
       defaultPrevented = utils.sendPointerEventToWindow(aEvent.type, left, top, button,
                                                         clickCount, modifiers, false,
@@ -418,24 +421,24 @@ function synthesizeTouchAtCenter(aTarget
  *
  * deltaMode must be defined, others are ok even if undefined.
  *
  * expectedOverflowDeltaX and expectedOverflowDeltaY take integer value.  The
  * value is just checked as 0 or positive or negative.
  *
  * aWindow is optional, and defaults to the current window object.
  */
-function synthesizeWheelAtPoint(aLeft, aTop, aEvent, aWindow)
+function synthesizeWheelAtPoint(aLeft, aTop, aEvent, aWindow = window)
 {
   var utils = _getDOMWindowUtils(aWindow);
   if (!utils) {
     return;
   }
 
-  var modifiers = _parseModifiers(aEvent);
+  var modifiers = _parseModifiers(aEvent, aWindow);
   var options = 0;
   if (aEvent.isNoLineOrPageDelta) {
     options |= utils.WHEEL_EVENT_CAUSED_BY_NO_LINE_OR_PAGE_DELTA_DEVICE;
   }
   if (aEvent.isMomentum) {
     options |= utils.WHEEL_EVENT_CAUSED_BY_MOMENTUM;
   }
   if (aEvent.isCustomizedByPrefs) {
@@ -518,19 +521,17 @@ function synthesizeWheel(aTarget, aOffse
  * This requires including paint_listener.js. Tests must call
  * DOMWindowUtils.restoreNormalRefresh() before finishing, if they use this
  * function.
  *
  * If no callback is provided, the caller is assumed to have its own method of
  * determining scroll completion and the refresh driver is not automatically
  * restored.
  */
-function sendWheelAndPaint(aTarget, aOffsetX, aOffsetY, aEvent, aCallback, aWindow) {
-  aWindow = aWindow || window;
-
+function sendWheelAndPaint(aTarget, aOffsetX, aOffsetY, aEvent, aCallback, aWindow = window) {
   var utils = _getDOMWindowUtils(aWindow);
   if (!utils)
     return;
 
   if (utils.isMozAfterPaintPending) {
     // If a paint is pending, then APZ may be waiting for a scroll acknowledgement
     // from the content thread. If we send a wheel event now, it could be ignored
     // by APZ (or its scroll offset could be overridden). To avoid problems we
@@ -568,19 +569,17 @@ function sendWheelAndPaint(aTarget, aOff
       }
     }, 0);
   };
 
   aWindow.addEventListener("wheel", onwheel);
   synthesizeWheel(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
 }
 
-function synthesizeNativeMouseMove(aTarget, aOffsetX, aOffsetY, aCallback, aWindow) {
-  aWindow = aWindow || window;
-
+function synthesizeNativeMouseMove(aTarget, aOffsetX, aOffsetY, aCallback, aWindow = window) {
   var utils = _getDOMWindowUtils(aWindow);
   if (!utils)
     return;
 
   var rect = aTarget.getBoundingClientRect();
   var x = aOffsetX + window.mozInnerScreenX + rect.left;
   var y = aOffsetY + window.mozInnerScreenY + rect.top;
   var scale = utils.screenPixelsPerCSSPixel;
@@ -595,85 +594,85 @@ function synthesizeNativeMouseMove(aTarg
   utils.sendNativeMouseMove(x * scale, y * scale, null, observer);
 }
 
 function _computeKeyCodeFromChar(aChar)
 {
   if (aChar.length != 1) {
     return 0;
   }
-  const nsIDOMKeyEvent = _EU_Ci.nsIDOMKeyEvent;
+  var KeyEvent = _EU_Ci.nsIDOMKeyEvent;
   if (aChar >= 'a' && aChar <= 'z') {
-    return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0);
+    return KeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0);
   }
   if (aChar >= 'A' && aChar <= 'Z') {
-    return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0);
+    return KeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0);
   }
   if (aChar >= '0' && aChar <= '9') {
-    return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0);
+    return KeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0);
   }
   // returns US keyboard layout's keycode
   switch (aChar) {
     case '~':
     case '`':
-      return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
+      return KeyEvent.DOM_VK_BACK_QUOTE;
     case '!':
-      return nsIDOMKeyEvent.DOM_VK_1;
+      return KeyEvent.DOM_VK_1;
     case '@':
-      return nsIDOMKeyEvent.DOM_VK_2;
+      return KeyEvent.DOM_VK_2;
     case '#':
-      return nsIDOMKeyEvent.DOM_VK_3;
+      return KeyEvent.DOM_VK_3;
     case '$':
-      return nsIDOMKeyEvent.DOM_VK_4;
+      return KeyEvent.DOM_VK_4;
     case '%':
-      return nsIDOMKeyEvent.DOM_VK_5;
+      return KeyEvent.DOM_VK_5;
     case '^':
-      return nsIDOMKeyEvent.DOM_VK_6;
+      return KeyEvent.DOM_VK_6;
     case '&':
-      return nsIDOMKeyEvent.DOM_VK_7;
+      return KeyEvent.DOM_VK_7;
     case '*':
-      return nsIDOMKeyEvent.DOM_VK_8;
+      return KeyEvent.DOM_VK_8;
     case '(':
-      return nsIDOMKeyEvent.DOM_VK_9;
+      return KeyEvent.DOM_VK_9;
     case ')':
-      return nsIDOMKeyEvent.DOM_VK_0;
+      return KeyEvent.DOM_VK_0;
     case '-':
     case '_':
-      return nsIDOMKeyEvent.DOM_VK_SUBTRACT;
+      return KeyEvent.DOM_VK_SUBTRACT;
     case '+':
     case '=':
-      return nsIDOMKeyEvent.DOM_VK_EQUALS;
+      return KeyEvent.DOM_VK_EQUALS;
     case '{':
     case '[':
-      return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
+      return KeyEvent.DOM_VK_OPEN_BRACKET;
     case '}':
     case ']':
-      return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
+      return KeyEvent.DOM_VK_CLOSE_BRACKET;
     case '|':
     case '\\':
-      return nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
+      return KeyEvent.DOM_VK_BACK_SLASH;
     case ':':
     case ';':
-      return nsIDOMKeyEvent.DOM_VK_SEMICOLON;
+      return KeyEvent.DOM_VK_SEMICOLON;
     case '\'':
     case '"':
-      return nsIDOMKeyEvent.DOM_VK_QUOTE;
+      return KeyEvent.DOM_VK_QUOTE;
     case '<':
     case ',':
-      return nsIDOMKeyEvent.DOM_VK_COMMA;
+      return KeyEvent.DOM_VK_COMMA;
     case '>':
     case '.':
-      return nsIDOMKeyEvent.DOM_VK_PERIOD;
+      return KeyEvent.DOM_VK_PERIOD;
     case '?':
     case '/':
-      return nsIDOMKeyEvent.DOM_VK_SLASH;
+      return KeyEvent.DOM_VK_SLASH;
     case '\n':
-      return nsIDOMKeyEvent.DOM_VK_RETURN;
+      return KeyEvent.DOM_VK_RETURN;
     case ' ':
-      return nsIDOMKeyEvent.DOM_VK_SPACE;
+      return KeyEvent.DOM_VK_SPACE;
     default:
       return 0;
   }
 }
 
 /**
  * Synthesize a key event. It is targeted at whatever would be targeted by an
  * actual keypress by the user, typically the focused element.
@@ -706,24 +705,25 @@ function _computeKeyCodeFromChar(aChar)
  *        the modifiers only during dispatching the key events.
  *        Note that if some of these values are false, they are ignored (i.e.,
  *        not inactivated with this function).
  *  - keyCode: Must be 0 - 255 (0xFF). If this is specified explicitly,
  *             .keyCode value is initialized with this value.
  *
  * aWindow is optional, and defaults to the current window object.
  */
-function synthesizeKey(aKey, aEvent, aWindow)
+function synthesizeKey(aKey, aEvent, aWindow = window)
 {
   var TIP = _getTIP(aWindow);
   if (!TIP) {
     return;
   }
-  var modifiers = _emulateToActivateModifiers(TIP, aEvent);
-  var keyEventDict = _createKeyboardEventDictionary(aKey, aEvent);
+  var KeyboardEvent = _getKeyboardEvent(aWindow);
+  var modifiers = _emulateToActivateModifiers(TIP, aEvent, aWindow);
+  var keyEventDict = _createKeyboardEventDictionary(aKey, aEvent, aWindow);
   var keyEvent = new KeyboardEvent("", keyEventDict.dictionary);
   var dispatchKeydown =
     !("type" in aEvent) || aEvent.type === "keydown" || !aEvent.type;
   var dispatchKeyup =
     !("type" in aEvent) || aEvent.type === "keyup"   || !aEvent.type;
 
   try {
     if (dispatchKeydown) {
@@ -735,22 +735,23 @@ function synthesizeKey(aKey, aEvent, aWi
           TIP.keydown(repeatedKeyEvent, keyEventDict.flags);
         }
       }
     }
     if (dispatchKeyup) {
       TIP.keyup(keyEvent, keyEventDict.flags);
     }
   } finally {
-    _emulateToInactivateModifiers(TIP, modifiers);
+    _emulateToInactivateModifiers(TIP, modifiers, aWindow);
   }
 }
 
-function _parseNativeModifiers(aModifiers)
+function _parseNativeModifiers(aModifiers, aWindow = window)
 {
+  var navigator = _getNavigator(aWindow);
   var modifiers;
   if (aModifiers.capsLockKey) {
     modifiers |= 0x00000001;
   }
   if (aModifiers.numLockKey) {
     modifiers |= 0x00000002;
   }
   if (aModifiers.shiftKey) {
@@ -859,22 +860,23 @@ const KEYBOARD_LAYOUT_THAI =
  *                              once the native keys have been processed
  *                              by Gecko. Will never be called if this
  *                              function returns false.
  * @return                      True if this function succeed dispatching
  *                              native key event.  Otherwise, false.
  */
 
 function synthesizeNativeKey(aKeyboardLayout, aNativeKeyCode, aModifiers,
-                             aChars, aUnmodifiedChars, aCallback)
+                             aChars, aUnmodifiedChars, aCallback, aWindow = window)
 {
-  var utils = _getDOMWindowUtils(window);
+  var utils = _getDOMWindowUtils(aWindow);
   if (!utils) {
     return false;
   }
+  var navigator = _getNavigator(aWindow);
   var nativeKeyboardLayout = null;
   if (navigator.platform.indexOf("Mac") == 0) {
     nativeKeyboardLayout = aKeyboardLayout.Mac;
   } else if (navigator.platform.indexOf("Win") == 0) {
     nativeKeyboardLayout = aKeyboardLayout.Win;
   }
   if (nativeKeyboardLayout === null) {
     return false;
@@ -883,17 +885,17 @@ function synthesizeNativeKey(aKeyboardLa
   var observer = {
     observe: function(aSubject, aTopic, aData) {
       if (aCallback && aTopic == "keyevent") {
         aCallback(aData);
       }
     }
   };
   utils.sendNativeKeyEvent(nativeKeyboardLayout, aNativeKeyCode,
-                           _parseNativeModifiers(aModifiers),
+                           _parseNativeModifiers(aModifiers, aWindow),
                            aChars, aUnmodifiedChars, observer);
   return true;
 }
 
 var _gSeenEvent = false;
 
 /**
  * Indicate that an event with an original target of aExpectedTarget and
@@ -984,36 +986,39 @@ function synthesizeKeyExpectEvent(key, a
 }
 
 function disableNonTestMouseEvents(aDisable)
 {
   var domutils = _getDOMWindowUtils();
   domutils.disableNonTestMouseEvents(aDisable);
 }
 
-function _getDOMWindowUtils(aWindow)
+function _getDOMWindowUtils(aWindow = window)
 {
+  // Leave this here as something, somewhere, passes a falsy argument
+  // to this, causing the |window| default argument not to get picked up.
   if (!aWindow) {
     aWindow = window;
   }
 
   // we need parent.SpecialPowers for:
   //  layout/base/tests/test_reftests_with_caret.html
   //  chrome: toolkit/content/tests/chrome/test_findbar.xul
   //  chrome: toolkit/content/tests/chrome/test_popup_anchor.xul
   if ("SpecialPowers" in window && window.SpecialPowers != undefined) {
     return SpecialPowers.getDOMWindowUtils(aWindow);
   }
   if ("SpecialPowers" in parent && parent.SpecialPowers != undefined) {
     return parent.SpecialPowers.getDOMWindowUtils(aWindow);
   }
 
-  //TODO: this is assuming we are in chrome space
-  return aWindow.QueryInterface(_EU_Ci.nsIInterfaceRequestor).
-                               getInterface(_EU_Ci.nsIDOMWindowUtils);
+  // TODO: this is assuming we are in chrome space
+  return aWindow
+      .QueryInterface(_EU_Ci.nsIInterfaceRequestor)
+      .getInterface(_EU_Ci.nsIDOMWindowUtils);
 }
 
 function _defineConstant(name, value) {
   Object.defineProperty(this, name, {
     value: value,
     enumerable: true,
     writable: false
   });
@@ -1050,18 +1055,35 @@ function _getTIP(aWindow, aCallback)
   }
   if (!tip.beginInputTransactionForTests(aWindow, aCallback)) {
     tip = null;
     TIPMap.delete(aWindow);
   }
   return tip;
 }
 
-function _guessKeyNameFromKeyCode(aKeyCode)
+function _getKeyboardEvent(aWindow = window)
+{
+  if (typeof KeyboardEvent != "undefined") {
+    return KeyboardEvent;
+  }
+  return aWindow.KeyboardEvent;
+}
+
+function _getNavigator(aWindow = window)
 {
+  if (typeof navigator != "undefined") {
+    return navigator;
+  }
+  return aWindow.navigator;
+}
+
+function _guessKeyNameFromKeyCode(aKeyCode, aWindow = window)
+{
+  var KeyboardEvent = _getKeyboardEvent(aWindow);
   switch (aKeyCode) {
     case KeyboardEvent.DOM_VK_CANCEL:
       return "Cancel";
     case KeyboardEvent.DOM_VK_HELP:
       return "Help";
     case KeyboardEvent.DOM_VK_BACK_SPACE:
       return "Backspace";
     case KeyboardEvent.DOM_VK_TAB:
@@ -1196,34 +1218,32 @@ function _guessKeyNameFromKeyCode(aKeyCo
       return "EraseEof";
     case KeyboardEvent.DOM_VK_PLAY:
       return "Play";
     default:
       return "Unidentified";
   }
 }
 
-function _createKeyboardEventDictionary(aKey, aKeyEvent)
-{
+function _createKeyboardEventDictionary(aKey, aKeyEvent, aWindow = window) {
   var result = { dictionary: null, flags: 0 };
-
   var keyCodeIsDefined = "keyCode" in aKeyEvent;
   var keyCode =
     (keyCodeIsDefined && aKeyEvent.keyCode >= 0 && aKeyEvent.keyCode <= 255) ?
       aKeyEvent.keyCode : 0;
   var keyName = "Unidentified";
   if (aKey.indexOf("KEY_") == 0) {
     keyName = aKey.substr("KEY_".length);
     result.flags |= _EU_Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
   } else if (aKey.indexOf("VK_") == 0) {
-    keyCode = KeyEvent["DOM_" + aKey];
+    keyCode = _EU_Ci.nsIDOMKeyEvent["DOM_" + aKey];
     if (!keyCode) {
       throw "Unknown key: " + aKey;
     }
-    keyName = _guessKeyNameFromKeyCode(keyCode);
+    keyName = _guessKeyNameFromKeyCode(keyCode, aWindow);
     result.flags |= _EU_Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
   } else if (aKey != "") {
     keyName = aKey;
     if (!keyCodeIsDefined) {
       keyCode = _computeKeyCodeFromChar(aKey.charAt(0));
     }
     if (!keyCode) {
       result.flags |= _EU_Ci.nsITextInputProcessor.KEY_KEEP_KEYCODE_ZERO;
@@ -1239,21 +1259,24 @@ function _createKeyboardEventDictionary(
     code: "code" in aKeyEvent ? aKeyEvent.code : "",
     location: locationIsDefined ? aKeyEvent.location : 0,
     repeat: "repeat" in aKeyEvent ? aKeyEvent.repeat === true : false,
     keyCode: keyCode,
   };
   return result;
 }
 
-function _emulateToActivateModifiers(aTIP, aKeyEvent)
+function _emulateToActivateModifiers(aTIP, aKeyEvent, aWindow = window)
 {
   if (!aKeyEvent) {
     return null;
   }
+  var KeyboardEvent = _getKeyboardEvent(aWindow);
+  var navigator = _getNavigator(aWindow);
+
   var modifiers = {
     normal: [
       { key: "Alt",        attr: "altKey" },
       { key: "AltGraph",   attr: "altGraphKey" },
       { key: "Control",    attr: "ctrlKey" },
       { key: "Fn",         attr: "fnKey" },
       { key: "Meta",       attr: "metaKey" },
       { key: "OS",         attr: "osKey" },
@@ -1295,21 +1318,22 @@ function _emulateToActivateModifiers(aTI
       aTIP.KEY_NON_PRINTABLE_KEY | aTIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
     aTIP.keyup(event,
       aTIP.KEY_NON_PRINTABLE_KEY | aTIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
     modifiers.lockable[i].activated = true;
   }
   return modifiers;
 }
 
-function _emulateToInactivateModifiers(aTIP, aModifiers)
+function _emulateToInactivateModifiers(aTIP, aModifiers, aWindow = window)
 {
   if (!aModifiers) {
     return;
   }
+  var KeyboardEvent = _getKeyboardEvent(aWindow);
   for (var i = 0; i < aModifiers.normal.length; i++) {
     if (!aModifiers.normal[i].activated) {
       continue;
     }
     var event = new KeyboardEvent("", { key: aModifiers.normal[i].key });
     aTIP.keyup(event,
       aTIP.KEY_NON_PRINTABLE_KEY | aTIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT);
   }
@@ -1344,27 +1368,28 @@ function _emulateToInactivateModifiers(a
  *                             dispatched.  This can emulates changing
  *                             composition state caused by key operation.
  *                             Its key value should start with "KEY_" if the
  *                             value is non-printable key name defined in D3E.
  * @param aWindow              Optional (If null, current |window| will be used)
  * @param aCallback            Optional (If non-null, use the callback for
  *                             receiving notifications to IME)
  */
-function synthesizeComposition(aEvent, aWindow, aCallback)
+function synthesizeComposition(aEvent, aWindow = window, aCallback)
 {
   var TIP = _getTIP(aWindow, aCallback);
   if (!TIP) {
     return false;
   }
-  var modifiers = _emulateToActivateModifiers(TIP, aEvent.key);
+  var KeyboardEvent = _getKeyboardEvent(aWindow);
+  var modifiers = _emulateToActivateModifiers(TIP, aEvent.key, aWindow);
   var ret = false;
   var keyEventDict =
     "key" in aEvent ?
-      _createKeyboardEventDictionary(aEvent.key.key, aEvent.key) :
+      _createKeyboardEventDictionary(aEvent.key.key, aEvent.key, aWindow) :
       { dictionary: null, flags: 0 };
   var keyEvent = 
     "key" in aEvent ?
       new KeyboardEvent(aEvent.type === "keydown" ? "keydown" : "",
                         keyEventDict.dictionary) :
       null;
   try {
     switch (aEvent.type) {
@@ -1375,17 +1400,17 @@ function synthesizeComposition(aEvent, a
         ret = TIP.commitComposition(keyEvent, keyEventDict.flags);
         break;
       case "compositioncommit":
         ret = TIP.commitCompositionWith(aEvent.data, keyEvent,
                                         keyEventDict.flags);
         break;
     }
   } finally {
-    _emulateToInactivateModifiers(TIP, modifiers);
+    _emulateToInactivateModifiers(TIP, modifiers, aWindow);
   }
 }
 /**
  * Synthesize a compositionchange event which causes a DOM text event and
  * compositionupdate event if it's necessary.
  *
  * @param aEvent   The compositionchange event's information, this has
  *                 |composition| and |caret| members.  |composition| has
@@ -1427,22 +1452,23 @@ function synthesizeComposition(aEvent, a
  *                 This can emulates changing composition state caused by key
  *                 operation.  Its key value should start with "KEY_" if the
  *                 value is non-printable key name defined in D3E.
  *
  * @param aWindow  Optional (If null, current |window| will be used)
  * @param aCallback     Optional (If non-null, use the callback for receiving
  *                      notifications to IME)
  */
-function synthesizeCompositionChange(aEvent, aWindow, aCallback)
+function synthesizeCompositionChange(aEvent, aWindow = window, aCallback)
 {
   var TIP = _getTIP(aWindow, aCallback);
   if (!TIP) {
     return;
   }
+  var KeyboardEvent = _getKeyboardEvent(aWindow);
 
   if (!aEvent.composition || !aEvent.composition.clauses ||
       !aEvent.composition.clauses[0]) {
     return;
   }
 
   TIP.setPendingCompositionString(aEvent.composition.string);
   if (aEvent.composition.clauses[0].length) {
@@ -1465,30 +1491,30 @@ function synthesizeCompositionChange(aEv
       }
     }
   }
 
   if (aEvent.caret) {
     TIP.setCaretInPendingComposition(aEvent.caret.start);
   }
 
-  var modifiers = _emulateToActivateModifiers(TIP, aEvent.key);
+  var modifiers = _emulateToActivateModifiers(TIP, aEvent.key, aWindow);
   try {
     var keyEventDict =
       "key" in aEvent ?
-        _createKeyboardEventDictionary(aEvent.key.key, aEvent.key) :
+        _createKeyboardEventDictionary(aEvent.key.key, aEvent.key, aWindow) :
         { dictionary: null, flags: 0 };
     var keyEvent = 
       "key" in aEvent ?
         new KeyboardEvent(aEvent.type === "keydown" ? "keydown" : "",
                           keyEventDict.dictionary) :
         null;
     TIP.flushPendingComposition(keyEvent, keyEventDict.flags);
   } finally {
-    _emulateToInactivateModifiers(TIP, modifiers);
+    _emulateToInactivateModifiers(TIP, modifiers, aWindow);
   }
 }
 
 // Must be synchronized with nsIDOMWindowUtils.
 const QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK          = 0x0000;
 const QUERY_CONTENT_FLAG_USE_XP_LINE_BREAK              = 0x0001;
 
 const SELECTION_SET_FLAG_USE_NATIVE_LINE_BREAK          = 0x0000;