services/sync/tps/extensions/mozmill/resource/stdlib/EventUtils.js

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

     1 // Export all available functions for Mozmill
     2 var EXPORTED_SYMBOLS = ["disableNonTestMouseEvents","sendMouseEvent", "sendChar",
     3                         "sendString", "sendKey", "synthesizeMouse", "synthesizeTouch",
     4                         "synthesizeMouseAtPoint", "synthesizeTouchAtPoint",
     5                         "synthesizeMouseAtCenter", "synthesizeTouchAtCenter",
     6                         "synthesizeWheel", "synthesizeKey",
     7                         "synthesizeMouseExpectEvent", "synthesizeKeyExpectEvent",
     8                         "synthesizeText",
     9                         "synthesizeComposition", "synthesizeQuerySelectedText"];
    11 const Ci = Components.interfaces;
    12 const Cc = Components.classes;
    14 var window = Cc["@mozilla.org/appshell/appShellService;1"]
    15              .getService(Ci.nsIAppShellService).hiddenDOMWindow;
    17 var _EU_Ci = Ci;
    18 var navigator = window.navigator;
    19 var KeyEvent = window.KeyEvent;
    20 var parent = window.parent;
    22 function is(aExpression1, aExpression2, aMessage) {
    23   if (aExpression1 !== aExpression2) {
    24     throw new Error(aMessage);
    25   }
    26 }
    28 /**
    29  * EventUtils provides some utility methods for creating and sending DOM events.
    30  * Current methods:
    31  *  sendMouseEvent
    32  *  sendChar
    33  *  sendString
    34  *  sendKey
    35  *  synthesizeMouse
    36  *  synthesizeMouseAtCenter
    37  *  synthesizeWheel
    38  *  synthesizeKey
    39  *  synthesizeMouseExpectEvent
    40  *  synthesizeKeyExpectEvent
    41  *
    42  *  When adding methods to this file, please add a performance test for it.
    43  */
    45 /**
    46  * Send a mouse event to the node aTarget (aTarget can be an id, or an
    47  * actual node) . The "event" passed in to aEvent is just a JavaScript
    48  * object with the properties set that the real mouse event object should
    49  * have. This includes the type of the mouse event.
    50  * E.g. to send an click event to the node with id 'node' you might do this:
    51  *
    52  * sendMouseEvent({type:'click'}, 'node');
    53  */
    54 function getElement(id) {
    55   return ((typeof(id) == "string") ?
    56     document.getElementById(id) : id); 
    57 };   
    59 this.$ = this.getElement;
    61 function sendMouseEvent(aEvent, aTarget, aWindow) {
    62   if (['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) {
    63     throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'");
    64   }
    66   if (!aWindow) {
    67     aWindow = window;
    68   }
    70   if (!(aTarget instanceof aWindow.Element)) {
    71     aTarget = aWindow.document.getElementById(aTarget);
    72   }
    74   var event = aWindow.document.createEvent('MouseEvent');
    76   var typeArg          = aEvent.type;
    77   var canBubbleArg     = true;
    78   var cancelableArg    = true;
    79   var viewArg          = aWindow;
    80   var detailArg        = aEvent.detail        || (aEvent.type == 'click'     ||
    81                                                   aEvent.type == 'mousedown' ||
    82                                                   aEvent.type == 'mouseup' ? 1 :
    83                                                   aEvent.type == 'dblclick'? 2 : 0);
    84   var screenXArg       = aEvent.screenX       || 0;
    85   var screenYArg       = aEvent.screenY       || 0;
    86   var clientXArg       = aEvent.clientX       || 0;
    87   var clientYArg       = aEvent.clientY       || 0;
    88   var ctrlKeyArg       = aEvent.ctrlKey       || false;
    89   var altKeyArg        = aEvent.altKey        || false;
    90   var shiftKeyArg      = aEvent.shiftKey      || false;
    91   var metaKeyArg       = aEvent.metaKey       || false;
    92   var buttonArg        = aEvent.button        || 0;
    93   var relatedTargetArg = aEvent.relatedTarget || null;
    95   event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg,
    96                        screenXArg, screenYArg, clientXArg, clientYArg,
    97                        ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
    98                        buttonArg, relatedTargetArg);
   100   SpecialPowers.dispatchEvent(aWindow, aTarget, event);
   101 }
   103 /**
   104  * Send the char aChar to the focused element.  This method handles casing of
   105  * chars (sends the right charcode, and sends a shift key for uppercase chars).
   106  * No other modifiers are handled at this point.
   107  *
   108  * For now this method only works for ASCII characters and emulates the shift
   109  * key state on US keyboard layout.
   110  */
   111 function sendChar(aChar, aWindow) {
   112   var hasShift;
   113   // Emulate US keyboard layout for the shiftKey state.
   114   switch (aChar) {
   115     case "!":
   116     case "@":
   117     case "#":
   118     case "$":
   119     case "%":
   120     case "^":
   121     case "&":
   122     case "*":
   123     case "(":
   124     case ")":
   125     case "_":
   126     case "+":
   127     case "{":
   128     case "}":
   129     case ":":
   130     case "\"":
   131     case "|":
   132     case "<":
   133     case ">":
   134     case "?":
   135       hasShift = true;
   136       break;
   137     default:
   138       hasShift = (aChar == aChar.toUpperCase());
   139       break;
   140   }
   141   synthesizeKey(aChar, { shiftKey: hasShift }, aWindow);
   142 }
   144 /**
   145  * Send the string aStr to the focused element.
   146  *
   147  * For now this method only works for ASCII characters and emulates the shift
   148  * key state on US keyboard layout.
   149  */
   150 function sendString(aStr, aWindow) {
   151   for (var i = 0; i < aStr.length; ++i) {
   152     sendChar(aStr.charAt(i), aWindow);
   153   }
   154 }
   156 /**
   157  * Send the non-character key aKey to the focused node.
   158  * The name of the key should be the part that comes after "DOM_VK_" in the
   159  *   KeyEvent constant name for this key.
   160  * No modifiers are handled at this point.
   161  */
   162 function sendKey(aKey, aWindow) {
   163   var keyName = "VK_" + aKey.toUpperCase();
   164   synthesizeKey(keyName, { shiftKey: false }, aWindow);
   165 }
   167 /**
   168  * Parse the key modifier flags from aEvent. Used to share code between
   169  * synthesizeMouse and synthesizeKey.
   170  */
   171 function _parseModifiers(aEvent)
   172 {
   173   const nsIDOMWindowUtils = _EU_Ci.nsIDOMWindowUtils;
   174   var mval = 0;
   175   if (aEvent.shiftKey) {
   176     mval |= nsIDOMWindowUtils.MODIFIER_SHIFT;
   177   }
   178   if (aEvent.ctrlKey) {
   179     mval |= nsIDOMWindowUtils.MODIFIER_CONTROL;
   180   }
   181   if (aEvent.altKey) {
   182     mval |= nsIDOMWindowUtils.MODIFIER_ALT;
   183   }
   184   if (aEvent.metaKey) {
   185     mval |= nsIDOMWindowUtils.MODIFIER_META;
   186   }
   187   if (aEvent.accelKey) {
   188     mval |= (navigator.platform.indexOf("Mac") >= 0) ?
   189       nsIDOMWindowUtils.MODIFIER_META : nsIDOMWindowUtils.MODIFIER_CONTROL;
   190   }
   191   if (aEvent.altGrKey) {
   192     mval |= nsIDOMWindowUtils.MODIFIER_ALTGRAPH;
   193   }
   194   if (aEvent.capsLockKey) {
   195     mval |= nsIDOMWindowUtils.MODIFIER_CAPSLOCK;
   196   }
   197   if (aEvent.fnKey) {
   198     mval |= nsIDOMWindowUtils.MODIFIER_FN;
   199   }
   200   if (aEvent.numLockKey) {
   201     mval |= nsIDOMWindowUtils.MODIFIER_NUMLOCK;
   202   }
   203   if (aEvent.scrollLockKey) {
   204     mval |= nsIDOMWindowUtils.MODIFIER_SCROLLLOCK;
   205   }
   206   if (aEvent.symbolLockKey) {
   207     mval |= nsIDOMWindowUtils.MODIFIER_SYMBOLLOCK;
   208   }
   209   if (aEvent.osKey) {
   210     mval |= nsIDOMWindowUtils.MODIFIER_OS;
   211   }
   213   return mval;
   214 }
   216 /**
   217  * Synthesize a mouse event on a target. The actual client point is determined
   218  * by taking the aTarget's client box and offseting it by aOffsetX and
   219  * aOffsetY. This allows mouse clicks to be simulated by calling this method.
   220  *
   221  * aEvent is an object which may contain the properties:
   222  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
   223  *
   224  * If the type is specified, an mouse event of that type is fired. Otherwise,
   225  * a mousedown followed by a mouse up is performed.
   226  *
   227  * aWindow is optional, and defaults to the current window object.
   228  *
   229  * Returns whether the event had preventDefault() called on it.
   230  */
   231 function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
   232 {
   233   var rect = aTarget.getBoundingClientRect();
   234   return synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
   235        aEvent, aWindow);
   236 }
   237 function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
   238 {
   239   var rect = aTarget.getBoundingClientRect();
   240   synthesizeTouchAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
   241        aEvent, aWindow);
   242 }
   244 /*
   245  * Synthesize a mouse event at a particular point in aWindow.
   246  *
   247  * aEvent is an object which may contain the properties:
   248  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
   249  *
   250  * If the type is specified, an mouse event of that type is fired. Otherwise,
   251  * a mousedown followed by a mouse up is performed.
   252  *
   253  * aWindow is optional, and defaults to the current window object.
   254  */
   255 function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
   256 {
   257   var utils = _getDOMWindowUtils(aWindow);
   258   var defaultPrevented = false;
   260   if (utils) {
   261     var button = aEvent.button || 0;
   262     var clickCount = aEvent.clickCount || 1;
   263     var modifiers = _parseModifiers(aEvent);
   264     var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0;
   265     var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0;
   267     if (("type" in aEvent) && aEvent.type) {
   268       defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers, false, pressure, inputSource);
   269     }
   270     else {
   271       utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers, false, pressure, inputSource);
   272       utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers, false, pressure, inputSource);
   273     }
   274   }
   276   return defaultPrevented;
   277 }
   278 function synthesizeTouchAtPoint(left, top, aEvent, aWindow)
   279 {
   280   var utils = _getDOMWindowUtils(aWindow);
   282   if (utils) {
   283     var id = aEvent.id || 0;
   284     var rx = aEvent.rx || 1;
   285     var ry = aEvent.rx || 1;
   286     var angle = aEvent.angle || 0;
   287     var force = aEvent.force || 1;
   288     var modifiers = _parseModifiers(aEvent);
   290     if (("type" in aEvent) && aEvent.type) {
   291       utils.sendTouchEvent(aEvent.type, [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
   292     }
   293     else {
   294       utils.sendTouchEvent("touchstart", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
   295       utils.sendTouchEvent("touchend", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
   296     }
   297   }
   298 }
   299 // Call synthesizeMouse with coordinates at the center of aTarget.
   300 function synthesizeMouseAtCenter(aTarget, aEvent, aWindow)
   301 {
   302   var rect = aTarget.getBoundingClientRect();
   303   synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent,
   304                   aWindow);
   305 }
   306 function synthesizeTouchAtCenter(aTarget, aEvent, aWindow)
   307 {
   308   var rect = aTarget.getBoundingClientRect();
   309   synthesizeTouch(aTarget, rect.width / 2, rect.height / 2, aEvent,
   310                   aWindow);
   311 }
   313 /**
   314  * Synthesize a wheel event on a target. The actual client point is determined
   315  * by taking the aTarget's client box and offseting it by aOffsetX and
   316  * aOffsetY.
   317  *
   318  * aEvent is an object which may contain the properties:
   319  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, deltaX, deltaY, deltaZ,
   320  *   deltaMode, lineOrPageDeltaX, lineOrPageDeltaY, isMomentum, isPixelOnlyDevice,
   321  *   isCustomizedByPrefs, expectedOverflowDeltaX, expectedOverflowDeltaY
   322  *
   323  * deltaMode must be defined, others are ok even if undefined.
   324  *
   325  * expectedOverflowDeltaX and expectedOverflowDeltaY take integer value.  The
   326  * value is just checked as 0 or positive or negative.
   327  *
   328  * aWindow is optional, and defaults to the current window object.
   329  */
   330 function synthesizeWheel(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
   331 {
   332   var utils = _getDOMWindowUtils(aWindow);
   333   if (!utils) {
   334     return;
   335   }
   337   var modifiers = _parseModifiers(aEvent);
   338   var options = 0;
   339   if (aEvent.isPixelOnlyDevice &&
   340       (aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL)) {
   341     options |= utils.WHEEL_EVENT_CAUSED_BY_PIXEL_ONLY_DEVICE;
   342   }
   343   if (aEvent.isMomentum) {
   344     options |= utils.WHEEL_EVENT_CAUSED_BY_MOMENTUM;
   345   }
   346   if (aEvent.isCustomizedByPrefs) {
   347     options |= utils.WHEEL_EVENT_CUSTOMIZED_BY_USER_PREFS;
   348   }
   349   if (typeof aEvent.expectedOverflowDeltaX !== "undefined") {
   350     if (aEvent.expectedOverflowDeltaX === 0) {
   351       options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO;
   352     } else if (aEvent.expectedOverflowDeltaX > 0) {
   353       options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE;
   354     } else {
   355       options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE;
   356     }
   357   }
   358   if (typeof aEvent.expectedOverflowDeltaY !== "undefined") {
   359     if (aEvent.expectedOverflowDeltaY === 0) {
   360       options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO;
   361     } else if (aEvent.expectedOverflowDeltaY > 0) {
   362       options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE;
   363     } else {
   364       options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE;
   365     }
   366   }
   367   var isPixelOnlyDevice =
   368     aEvent.isPixelOnlyDevice && aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL;
   370   // Avoid the JS warnings "reference to undefined property"
   371   if (!aEvent.deltaX) {
   372     aEvent.deltaX = 0;
   373   }
   374   if (!aEvent.deltaY) {
   375     aEvent.deltaY = 0;
   376   }
   377   if (!aEvent.deltaZ) {
   378     aEvent.deltaZ = 0;
   379   }
   381   var lineOrPageDeltaX =
   382     aEvent.lineOrPageDeltaX != null ? aEvent.lineOrPageDeltaX :
   383                   aEvent.deltaX > 0 ? Math.floor(aEvent.deltaX) :
   384                                       Math.ceil(aEvent.deltaX);
   385   var lineOrPageDeltaY =
   386     aEvent.lineOrPageDeltaY != null ? aEvent.lineOrPageDeltaY :
   387                   aEvent.deltaY > 0 ? Math.floor(aEvent.deltaY) :
   388                                       Math.ceil(aEvent.deltaY);
   390   var rect = aTarget.getBoundingClientRect();
   391   utils.sendWheelEvent(rect.left + aOffsetX, rect.top + aOffsetY,
   392                        aEvent.deltaX, aEvent.deltaY, aEvent.deltaZ,
   393                        aEvent.deltaMode, modifiers,
   394                        lineOrPageDeltaX, lineOrPageDeltaY, options);
   395 }
   397 function _computeKeyCodeFromChar(aChar)
   398 {
   399   if (aChar.length != 1) {
   400     return 0;
   401   }
   402   const nsIDOMKeyEvent = _EU_Ci.nsIDOMKeyEvent;
   403   if (aChar >= 'a' && aChar <= 'z') {
   404     return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0);
   405   }
   406   if (aChar >= 'A' && aChar <= 'Z') {
   407     return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0);
   408   }
   409   if (aChar >= '0' && aChar <= '9') {
   410     return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0);
   411   }
   412   // returns US keyboard layout's keycode
   413   switch (aChar) {
   414     case '~':
   415     case '`':
   416       return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
   417     case '!':
   418       return nsIDOMKeyEvent.DOM_VK_1;
   419     case '@':
   420       return nsIDOMKeyEvent.DOM_VK_2;
   421     case '#':
   422       return nsIDOMKeyEvent.DOM_VK_3;
   423     case '$':
   424       return nsIDOMKeyEvent.DOM_VK_4;
   425     case '%':
   426       return nsIDOMKeyEvent.DOM_VK_5;
   427     case '^':
   428       return nsIDOMKeyEvent.DOM_VK_6;
   429     case '&':
   430       return nsIDOMKeyEvent.DOM_VK_7;
   431     case '*':
   432       return nsIDOMKeyEvent.DOM_VK_8;
   433     case '(':
   434       return nsIDOMKeyEvent.DOM_VK_9;
   435     case ')':
   436       return nsIDOMKeyEvent.DOM_VK_0;
   437     case '-':
   438     case '_':
   439       return nsIDOMKeyEvent.DOM_VK_SUBTRACT;
   440     case '+':
   441     case '=':
   442       return nsIDOMKeyEvent.DOM_VK_EQUALS;
   443     case '{':
   444     case '[':
   445       return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
   446     case '}':
   447     case ']':
   448       return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
   449     case '|':
   450     case '\\':
   451       return nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
   452     case ':':
   453     case ';':
   454       return nsIDOMKeyEvent.DOM_VK_SEMICOLON;
   455     case '\'':
   456     case '"':
   457       return nsIDOMKeyEvent.DOM_VK_QUOTE;
   458     case '<':
   459     case ',':
   460       return nsIDOMKeyEvent.DOM_VK_COMMA;
   461     case '>':
   462     case '.':
   463       return nsIDOMKeyEvent.DOM_VK_PERIOD;
   464     case '?':
   465     case '/':
   466       return nsIDOMKeyEvent.DOM_VK_SLASH;
   467     default:
   468       return 0;
   469   }
   470 }
   472 /**
   473  * isKeypressFiredKey() returns TRUE if the given key should cause keypress
   474  * event when widget handles the native key event.  Otherwise, FALSE.
   475  *
   476  * aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key
   477  * name begins with "VK_", or a character.
   478  */
   479 function isKeypressFiredKey(aDOMKeyCode)
   480 {
   481   if (typeof(aDOMKeyCode) == "string") {
   482     if (aDOMKeyCode.indexOf("VK_") == 0) {
   483       aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode];
   484       if (!aDOMKeyCode) {
   485         throw "Unknown key: " + aDOMKeyCode;
   486       }
   487     } else {
   488       // If the key generates a character, it must cause a keypress event.
   489       return true;
   490     }
   491   }
   492   switch (aDOMKeyCode) {
   493     case KeyEvent.DOM_VK_SHIFT:
   494     case KeyEvent.DOM_VK_CONTROL:
   495     case KeyEvent.DOM_VK_ALT:
   496     case KeyEvent.DOM_VK_CAPS_LOCK:
   497     case KeyEvent.DOM_VK_NUM_LOCK:
   498     case KeyEvent.DOM_VK_SCROLL_LOCK:
   499     case KeyEvent.DOM_VK_META:
   500       return false;
   501     default:
   502       return true;
   503   }
   504 }
   506 /**
   507  * Synthesize a key event. It is targeted at whatever would be targeted by an
   508  * actual keypress by the user, typically the focused element.
   509  *
   510  * aKey should be either a character or a keycode starting with VK_ such as
   511  * VK_ENTER.
   512  *
   513  * aEvent is an object which may contain the properties:
   514  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, type, location
   515  *
   516  * Sets one of KeyboardEvent.DOM_KEY_LOCATION_* to location.  Otherwise,
   517  * DOMWindowUtils will choose good location from the keycode.
   518  *
   519  * If the type is specified, a key event of that type is fired. Otherwise,
   520  * a keydown, a keypress and then a keyup event are fired in sequence.
   521  *
   522  * aWindow is optional, and defaults to the current window object.
   523  */
   524 function synthesizeKey(aKey, aEvent, aWindow)
   525 {
   526   var utils = _getDOMWindowUtils(aWindow);
   527   if (utils) {
   528     var keyCode = 0, charCode = 0;
   529     if (aKey.indexOf("VK_") == 0) {
   530       keyCode = KeyEvent["DOM_" + aKey];
   531       if (!keyCode) {
   532         throw "Unknown key: " + aKey;
   533       }
   534     } else {
   535       charCode = aKey.charCodeAt(0);
   536       keyCode = _computeKeyCodeFromChar(aKey.charAt(0));
   537     }
   539     var modifiers = _parseModifiers(aEvent);
   540     var flags = 0;
   541     if (aEvent.location != undefined) {
   542       switch (aEvent.location) {
   543         case KeyboardEvent.DOM_KEY_LOCATION_STANDARD:
   544           flags |= utils.KEY_FLAG_LOCATION_STANDARD;
   545           break;
   546         case KeyboardEvent.DOM_KEY_LOCATION_LEFT:
   547           flags |= utils.KEY_FLAG_LOCATION_LEFT;
   548           break;
   549         case KeyboardEvent.DOM_KEY_LOCATION_RIGHT:
   550           flags |= utils.KEY_FLAG_LOCATION_RIGHT;
   551           break;
   552         case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD:
   553           flags |= utils.KEY_FLAG_LOCATION_NUMPAD;
   554           break;
   555         case KeyboardEvent.DOM_KEY_LOCATION_MOBILE:
   556           flags |= utils.KEY_FLAG_LOCATION_MOBILE;
   557           break;
   558         case KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK:
   559           flags |= utils.KEY_FLAG_LOCATION_JOYSTICK;
   560           break;
   561       }
   562     }
   564     if (!("type" in aEvent) || !aEvent.type) {
   565       // Send keydown + (optional) keypress + keyup events.
   566       var keyDownDefaultHappened =
   567         utils.sendKeyEvent("keydown", keyCode, 0, modifiers, flags);
   568       if (isKeypressFiredKey(keyCode)) {
   569         if (!keyDownDefaultHappened) {
   570           flags |= utils.KEY_FLAG_PREVENT_DEFAULT;
   571         }
   572         utils.sendKeyEvent("keypress", keyCode, charCode, modifiers, flags);
   573       }
   574       utils.sendKeyEvent("keyup", keyCode, 0, modifiers, flags);
   575     } else if (aEvent.type == "keypress") {
   576       // Send standalone keypress event.
   577       utils.sendKeyEvent(aEvent.type, keyCode, charCode, modifiers, flags);
   578     } else {
   579       // Send other standalone event than keypress.
   580       utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers, flags);
   581     }
   582   }
   583 }
   585 var _gSeenEvent = false;
   587 /**
   588  * Indicate that an event with an original target of aExpectedTarget and
   589  * a type of aExpectedEvent is expected to be fired, or not expected to
   590  * be fired.
   591  */
   592 function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName)
   593 {
   594   if (!aExpectedTarget || !aExpectedEvent)
   595     return null;
   597   _gSeenEvent = false;
   599   var type = (aExpectedEvent.charAt(0) == "!") ?
   600              aExpectedEvent.substring(1) : aExpectedEvent;
   601   var eventHandler = function(event) {
   602     var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget &&
   603                    event.type == type);
   604     is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : ""));
   605     _gSeenEvent = true;
   606   };
   608   aExpectedTarget.addEventListener(type, eventHandler, false);
   609   return eventHandler;
   610 }
   612 /**
   613  * Check if the event was fired or not. The event handler aEventHandler
   614  * will be removed.
   615  */
   616 function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName)
   617 {
   618   if (aEventHandler) {
   619     var expectEvent = (aExpectedEvent.charAt(0) != "!");
   620     var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1);
   621     aExpectedTarget.removeEventListener(type, aEventHandler, false);
   622     var desc = type + " event";
   623     if (!expectEvent)
   624       desc += " not";
   625     is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired");
   626   }
   628   _gSeenEvent = false;
   629 }
   631 /**
   632  * Similar to synthesizeMouse except that a test is performed to see if an
   633  * event is fired at the right target as a result.
   634  *
   635  * aExpectedTarget - the expected originalTarget of the event.
   636  * aExpectedEvent - the expected type of the event, such as 'select'.
   637  * aTestName - the test name when outputing results
   638  *
   639  * To test that an event is not fired, use an expected type preceded by an
   640  * exclamation mark, such as '!select'. This might be used to test that a
   641  * click on a disabled element doesn't fire certain events for instance.
   642  *
   643  * aWindow is optional, and defaults to the current window object.
   644  */
   645 function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent,
   646                                     aExpectedTarget, aExpectedEvent, aTestName,
   647                                     aWindow)
   648 {
   649   var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
   650   synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
   651   _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
   652 }
   654 /**
   655  * Similar to synthesizeKey except that a test is performed to see if an
   656  * event is fired at the right target as a result.
   657  *
   658  * aExpectedTarget - the expected originalTarget of the event.
   659  * aExpectedEvent - the expected type of the event, such as 'select'.
   660  * aTestName - the test name when outputing results
   661  *
   662  * To test that an event is not fired, use an expected type preceded by an
   663  * exclamation mark, such as '!select'.
   664  *
   665  * aWindow is optional, and defaults to the current window object.
   666  */
   667 function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent,
   668                                   aTestName, aWindow)
   669 {
   670   var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
   671   synthesizeKey(key, aEvent, aWindow);
   672   _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
   673 }
   675 function disableNonTestMouseEvents(aDisable)
   676 {
   677   var domutils = _getDOMWindowUtils();
   678   domutils.disableNonTestMouseEvents(aDisable);
   679 }
   681 function _getDOMWindowUtils(aWindow)
   682 {
   683   if (!aWindow) {
   684     aWindow = window;
   685   }
   687   // we need parent.SpecialPowers for:
   688   //  layout/base/tests/test_reftests_with_caret.html
   689   //  chrome: toolkit/content/tests/chrome/test_findbar.xul
   690   //  chrome: toolkit/content/tests/chrome/test_popup_anchor.xul
   691   if ("SpecialPowers" in window && window.SpecialPowers != undefined) {
   692     return SpecialPowers.getDOMWindowUtils(aWindow);
   693   }
   694   if ("SpecialPowers" in parent && parent.SpecialPowers != undefined) {
   695     return parent.SpecialPowers.getDOMWindowUtils(aWindow);
   696   }
   698   //TODO: this is assuming we are in chrome space
   699   return aWindow.QueryInterface(_EU_Ci.nsIInterfaceRequestor).
   700                                getInterface(_EU_Ci.nsIDOMWindowUtils);
   701 }
   703 // Must be synchronized with nsIDOMWindowUtils.
   704 const COMPOSITION_ATTR_RAWINPUT              = 0x02;
   705 const COMPOSITION_ATTR_SELECTEDRAWTEXT       = 0x03;
   706 const COMPOSITION_ATTR_CONVERTEDTEXT         = 0x04;
   707 const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
   709 /**
   710  * Synthesize a composition event.
   711  *
   712  * @param aEvent               The composition event information.  This must
   713  *                             have |type| member.  The value must be
   714  *                             "compositionstart", "compositionend" or
   715  *                             "compositionupdate".
   716  *                             And also this may have |data| and |locale| which
   717  *                             would be used for the value of each property of
   718  *                             the composition event.  Note that the data would
   719  *                             be ignored if the event type were
   720  *                             "compositionstart".
   721  * @param aWindow              Optional (If null, current |window| will be used)
   722  */
   723 function synthesizeComposition(aEvent, aWindow)
   724 {
   725   var utils = _getDOMWindowUtils(aWindow);
   726   if (!utils) {
   727     return;
   728   }
   730   utils.sendCompositionEvent(aEvent.type, aEvent.data ? aEvent.data : "",
   731                              aEvent.locale ? aEvent.locale : "");
   732 }
   733 /**
   734  * Synthesize a text event.
   735  *
   736  * @param aEvent   The text event's information, this has |composition|
   737  *                 and |caret| members.  |composition| has |string| and
   738  *                 |clauses| members.  |clauses| must be array object.  Each
   739  *                 object has |length| and |attr|.  And |caret| has |start| and
   740  *                 |length|.  See the following tree image.
   741  *
   742  *                 aEvent
   743  *                   +-- composition
   744  *                   |     +-- string
   745  *                   |     +-- clauses[]
   746  *                   |           +-- length
   747  *                   |           +-- attr
   748  *                   +-- caret
   749  *                         +-- start
   750  *                         +-- length
   751  *
   752  *                 Set the composition string to |composition.string|.  Set its
   753  *                 clauses information to the |clauses| array.
   754  *
   755  *                 When it's composing, set the each clauses' length to the
   756  *                 |composition.clauses[n].length|.  The sum of the all length
   757  *                 values must be same as the length of |composition.string|.
   758  *                 Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the
   759  *                 |composition.clauses[n].attr|.
   760  *
   761  *                 When it's not composing, set 0 to the
   762  *                 |composition.clauses[0].length| and
   763  *                 |composition.clauses[0].attr|.
   764  *
   765  *                 Set caret position to the |caret.start|. It's offset from
   766  *                 the start of the composition string.  Set caret length to
   767  *                 |caret.length|.  If it's larger than 0, it should be wide
   768  *                 caret.  However, current nsEditor doesn't support wide
   769  *                 caret, therefore, you should always set 0 now.
   770  *
   771  * @param aWindow  Optional (If null, current |window| will be used)
   772  */
   773 function synthesizeText(aEvent, aWindow)
   774 {
   775   var utils = _getDOMWindowUtils(aWindow);
   776   if (!utils) {
   777     return;
   778   }
   780   if (!aEvent.composition || !aEvent.composition.clauses ||
   781       !aEvent.composition.clauses[0]) {
   782     return;
   783   }
   785   var firstClauseLength = aEvent.composition.clauses[0].length;
   786   var firstClauseAttr   = aEvent.composition.clauses[0].attr;
   787   var secondClauseLength = 0;
   788   var secondClauseAttr = 0;
   789   var thirdClauseLength = 0;
   790   var thirdClauseAttr = 0;
   791   if (aEvent.composition.clauses[1]) {
   792     secondClauseLength = aEvent.composition.clauses[1].length;
   793     secondClauseAttr   = aEvent.composition.clauses[1].attr;
   794     if (aEvent.composition.clauses[2]) {
   795       thirdClauseLength = aEvent.composition.clauses[2].length;
   796       thirdClauseAttr   = aEvent.composition.clauses[2].attr;
   797     }
   798   }
   800   var caretStart = -1;
   801   var caretLength = 0;
   802   if (aEvent.caret) {
   803     caretStart = aEvent.caret.start;
   804     caretLength = aEvent.caret.length;
   805   }
   807   utils.sendTextEvent(aEvent.composition.string,
   808                       firstClauseLength, firstClauseAttr,
   809                       secondClauseLength, secondClauseAttr,
   810                       thirdClauseLength, thirdClauseAttr,
   811                       caretStart, caretLength);
   812 }
   814 /**
   815  * Synthesize a query selected text event.
   816  *
   817  * @param aWindow  Optional (If null, current |window| will be used)
   818  * @return         An nsIQueryContentEventResult object.  If this failed,
   819  *                 the result might be null.
   820  */
   821 function synthesizeQuerySelectedText(aWindow)
   822 {
   823   var utils = _getDOMWindowUtils(aWindow);
   824   if (!utils) {
   825     return null;
   826   }
   828   return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0);
   829 }

mercurial