testing/marionette/EventUtils.js

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /**
     6  * EventUtils provides some utility methods for creating and sending DOM events.
     7  * Current methods:
     8  *  sendMouseEvent
     9  *  sendChar
    10  *  sendString
    11  *  sendKey
    12  *  synthesizeMouse
    13  *  synthesizeMouseAtCenter
    14  *  synthesizeMouseScroll
    15  *  synthesizeKey
    16  *  synthesizeMouseExpectEvent
    17  *  synthesizeKeyExpectEvent
    18  *
    19  *  When adding methods to this file, please add a performance test for it.
    20  */
    22 /**
    23  * Send a mouse event to the node aTarget (aTarget can be an id, or an
    24  * actual node) . The "event" passed in to aEvent is just a JavaScript
    25  * object with the properties set that the real mouse event object should
    26  * have. This includes the type of the mouse event.
    27  * E.g. to send an click event to the node with id 'node' you might do this:
    28  *
    29  * sendMouseEvent({type:'click'}, 'node');
    30  */
    31 function getElement(id) {
    32   return ((typeof(id) == "string") ?
    33     document.getElementById(id) : id); 
    34 };   
    36 this.$ = this.getElement;
    37 const KeyEvent = Components.interfaces.nsIDOMKeyEvent;
    39 function sendMouseEvent(aEvent, aTarget, aWindow) {
    40   if (['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) {
    41     throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'");
    42   }
    44   if (!aWindow) {
    45     aWindow = window;
    46   }
    48   if (!(aTarget instanceof Element)) {
    49     aTarget = aWindow.document.getElementById(aTarget);
    50   }
    52   var event = aWindow.document.createEvent('MouseEvent');
    54   var typeArg          = aEvent.type;
    55   var canBubbleArg     = true;
    56   var cancelableArg    = true;
    57   var viewArg          = aWindow;
    58   var detailArg        = aEvent.detail        || (aEvent.type == 'click'     ||
    59                                                   aEvent.type == 'mousedown' ||
    60                                                   aEvent.type == 'mouseup' ? 1 :
    61                                                   aEvent.type == 'dblclick'? 2 : 0);
    62   var screenXArg       = aEvent.screenX       || 0;
    63   var screenYArg       = aEvent.screenY       || 0;
    64   var clientXArg       = aEvent.clientX       || 0;
    65   var clientYArg       = aEvent.clientY       || 0;
    66   var ctrlKeyArg       = aEvent.ctrlKey       || false;
    67   var altKeyArg        = aEvent.altKey        || false;
    68   var shiftKeyArg      = aEvent.shiftKey      || false;
    69   var metaKeyArg       = aEvent.metaKey       || false;
    70   var buttonArg        = aEvent.button        || 0;
    71   var relatedTargetArg = aEvent.relatedTarget || null;
    73   event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg,
    74                        screenXArg, screenYArg, clientXArg, clientYArg,
    75                        ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
    76                        buttonArg, relatedTargetArg);
    78   //removed: SpecialPowers.dispatchEvent(aWindow, aTarget, event);
    79 }
    81 /**
    82  * Send the char aChar to the focused element.  This method handles casing of
    83  * chars (sends the right charcode, and sends a shift key for uppercase chars).
    84  * No other modifiers are handled at this point.
    85  *
    86  * For now this method only works for English letters (lower and upper case)
    87  * and the digits 0-9.
    88  */
    89 function sendChar(aChar, aWindow) {
    90   // DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9.
    91   var hasShift = (aChar == aChar.toUpperCase());
    92   synthesizeKey(aChar, { shiftKey: hasShift }, aWindow);
    93 }
    95 /**
    96  * Send the string aStr to the focused element.
    97  *
    98  * For now this method only works for English letters (lower and upper case)
    99  * and the digits 0-9.
   100  */
   101 function sendString(aStr, aWindow) {
   102   for (var i = 0; i < aStr.length; ++i) {
   103     sendChar(aStr.charAt(i), aWindow);
   104   }
   105 }
   107 /**
   108  * Send the non-character key aKey to the focused node.
   109  * The name of the key should be the part that comes after "DOM_VK_" in the
   110  *   KeyEvent constant name for this key.
   111  * No modifiers are handled at this point.
   112  */
   113 function sendKey(aKey, aWindow) {
   114   var keyName = "VK_" + aKey.toUpperCase();
   115   synthesizeKey(keyName, { shiftKey: false }, aWindow);
   116 }
   118 /**
   119  * Parse the key modifier flags from aEvent. Used to share code between
   120  * synthesizeMouse and synthesizeKey.
   121  */
   122 function _parseModifiers(aEvent)
   123 {
   124   const masks = Components.interfaces.nsIDOMNSEvent;
   125   var mval = 0;
   126   if (aEvent.shiftKey)
   127     mval |= masks.SHIFT_MASK;
   128   if (aEvent.ctrlKey)
   129     mval |= masks.CONTROL_MASK;
   130   if (aEvent.altKey)
   131     mval |= masks.ALT_MASK;
   132   if (aEvent.metaKey)
   133     mval |= masks.META_MASK;
   134   if (aEvent.accelKey)
   135     mval |= (navigator.platform.indexOf("Mac") >= 0) ? masks.META_MASK :
   136                                                        masks.CONTROL_MASK;
   138   return mval;
   139 }
   141 /**
   142  * Synthesize a mouse event on a target. The actual client point is determined
   143  * by taking the aTarget's client box and offseting it by aOffsetX and
   144  * aOffsetY. This allows mouse clicks to be simulated by calling this method.
   145  *
   146  * aEvent is an object which may contain the properties:
   147  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
   148  *
   149  * If the type is specified, an mouse event of that type is fired. Otherwise,
   150  * a mousedown followed by a mouse up is performed.
   151  *
   152  * aWindow is optional, and defaults to the current window object.
   153  */
   154 function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
   155 {
   156   var rect = aTarget.getBoundingClientRect();
   157   synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
   158 			 aEvent, aWindow);
   159 }
   161 /*
   162  * Synthesize a mouse event at a particular point in aWindow.
   163  *
   164  * aEvent is an object which may contain the properties:
   165  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
   166  *
   167  * If the type is specified, an mouse event of that type is fired. Otherwise,
   168  * a mousedown followed by a mouse up is performed.
   169  *
   170  * aWindow is optional, and defaults to the current window object.
   171  */
   172 function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
   173 {
   174   var utils = _getDOMWindowUtils(aWindow);
   176   if (utils) {
   177     var button = aEvent.button || 0;
   178     var clickCount = aEvent.clickCount || 1;
   179     var modifiers = _parseModifiers(aEvent);
   181     if (("type" in aEvent) && aEvent.type) {
   182       utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
   183     }
   184     else {
   185       utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers);
   186       utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers);
   187     }
   188   }
   189 }
   191 // Call synthesizeMouse with coordinates at the center of aTarget.
   192 function synthesizeMouseAtCenter(aTarget, aEvent, aWindow)
   193 {
   194   var rect = aTarget.getBoundingClientRect();
   195   synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent,
   196                   aWindow);
   197 }
   199 /**
   200  * Synthesize a mouse scroll event on a target. The actual client point is determined
   201  * by taking the aTarget's client box and offseting it by aOffsetX and
   202  * aOffsetY.
   203  *
   204  * aEvent is an object which may contain the properties:
   205  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, button, type, axis, delta, hasPixels
   206  *
   207  * If the type is specified, a mouse scroll event of that type is fired. Otherwise,
   208  * "DOMMouseScroll" is used.
   209  *
   210  * If the axis is specified, it must be one of "horizontal" or "vertical". If not specified,
   211  * "vertical" is used.
   212  *
   213  * 'delta' is the amount to scroll by (can be positive or negative). It must
   214  * be specified.
   215  *
   216  * 'hasPixels' specifies whether kHasPixels should be set in the scrollFlags.
   217  *
   218  * 'isMomentum' specifies whether kIsMomentum should be set in the scrollFlags.
   219  *
   220  * aWindow is optional, and defaults to the current window object.
   221  */
   222 function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
   223 {
   224   var utils = _getDOMWindowUtils(aWindow);
   226   if (utils) {
   227     // See nsMouseScrollFlags in nsGUIEvent.h
   228     const kIsVertical = 0x02;
   229     const kIsHorizontal = 0x04;
   230     const kHasPixels = 0x08;
   231     const kIsMomentum = 0x40;
   233     var button = aEvent.button || 0;
   234     var modifiers = _parseModifiers(aEvent);
   236     var rect = aTarget.getBoundingClientRect();
   238     var left = rect.left;
   239     var top = rect.top;
   241     var type = (("type" in aEvent) && aEvent.type) || "DOMMouseScroll";
   242     var axis = aEvent.axis || "vertical";
   243     var scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical;
   244     if (aEvent.hasPixels) {
   245       scrollFlags |= kHasPixels;
   246     }
   247     if (aEvent.isMomentum) {
   248       scrollFlags |= kIsMomentum;
   249     }
   250     utils.sendMouseScrollEvent(type, left + aOffsetX, top + aOffsetY, button,
   251                                scrollFlags, aEvent.delta, modifiers);
   252   }
   253 }
   255 function _computeKeyCodeFromChar(aChar)
   256 {
   257   if (aChar.length != 1) {
   258     return 0;
   259   }
   260   const nsIDOMKeyEvent = Components.interfaces.nsIDOMKeyEvent;
   261   if (aChar >= 'a' && aChar <= 'z') {
   262     return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0);
   263   }
   264   if (aChar >= 'A' && aChar <= 'Z') {
   265     return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0);
   266   }
   267   if (aChar >= '0' && aChar <= '9') {
   268     return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0);
   269   }
   270   // returns US keyboard layout's keycode
   271   switch (aChar) {
   272     case '~':
   273     case '`':
   274       return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
   275     case '!':
   276       return nsIDOMKeyEvent.DOM_VK_1;
   277     case '@':
   278       return nsIDOMKeyEvent.DOM_VK_2;
   279     case '#':
   280       return nsIDOMKeyEvent.DOM_VK_3;
   281     case '$':
   282       return nsIDOMKeyEvent.DOM_VK_4;
   283     case '%':
   284       return nsIDOMKeyEvent.DOM_VK_5;
   285     case '^':
   286       return nsIDOMKeyEvent.DOM_VK_6;
   287     case '&':
   288       return nsIDOMKeyEvent.DOM_VK_7;
   289     case '*':
   290       return nsIDOMKeyEvent.DOM_VK_8;
   291     case '(':
   292       return nsIDOMKeyEvent.DOM_VK_9;
   293     case ')':
   294       return nsIDOMKeyEvent.DOM_VK_0;
   295     case '-':
   296     case '_':
   297       return nsIDOMKeyEvent.DOM_VK_SUBTRACT;
   298     case '+':
   299     case '=':
   300       return nsIDOMKeyEvent.DOM_VK_EQUALS;
   301     case '{':
   302     case '[':
   303       return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
   304     case '}':
   305     case ']':
   306       return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
   307     case '|':
   308     case '\\':
   309       return nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
   310     case ':':
   311     case ';':
   312       return nsIDOMKeyEvent.DOM_VK_SEMICOLON;
   313     case '\'':
   314     case '"':
   315       return nsIDOMKeyEvent.DOM_VK_QUOTE;
   316     case '<':
   317     case ',':
   318       return nsIDOMKeyEvent.DOM_VK_COMMA;
   319     case '>':
   320     case '.':
   321       return nsIDOMKeyEvent.DOM_VK_PERIOD;
   322     case '?':
   323     case '/':
   324       return nsIDOMKeyEvent.DOM_VK_SLASH;
   325     case '\n':
   326       return nsIDOMKeyEvent.DOM_VK_RETURN;
   327     default:
   328       return 0;
   329   }
   330 }
   332 /**
   333  * isKeypressFiredKey() returns TRUE if the given key should cause keypress
   334  * event when widget handles the native key event.  Otherwise, FALSE.
   335  *
   336  * aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key
   337  * name begins with "VK_", or a character.
   338  */
   339 function isKeypressFiredKey(aDOMKeyCode)
   340 {
   341   const KeyEvent = Components.interfaces.nsIDOMKeyEvent;
   342   if (typeof(aDOMKeyCode) == "string") {
   343     if (aDOMKeyCode.indexOf("VK_") == 0) {
   344       aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode];
   345       if (!aDOMKeyCode) {
   346         throw "Unknown key: " + aDOMKeyCode;
   347       }
   348     } else {
   349       // If the key generates a character, it must cause a keypress event.
   350       return true;
   351     }
   352   }
   353   switch (aDOMKeyCode) {
   354     case KeyEvent.DOM_VK_SHIFT:
   355     case KeyEvent.DOM_VK_CONTROL:
   356     case KeyEvent.DOM_VK_ALT:
   357     case KeyEvent.DOM_VK_CAPS_LOCK:
   358     case KeyEvent.DOM_VK_NUM_LOCK:
   359     case KeyEvent.DOM_VK_SCROLL_LOCK:
   360     case KeyEvent.DOM_VK_META:
   361       return false;
   362     default:
   363       return true;
   364   }
   365 }
   367 /**
   368  * Synthesize a key event. It is targeted at whatever would be targeted by an
   369  * actual keypress by the user, typically the focused element.
   370  *
   371  * aKey should be either a character or a keycode starting with VK_ such as
   372  * VK_RETURN.
   373  *
   374  * aEvent is an object which may contain the properties:
   375  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, type
   376  *
   377  * If the type is specified, a key event of that type is fired. Otherwise,
   378  * a keydown, a keypress and then a keyup event are fired in sequence.
   379  *
   380  * aWindow is optional, and defaults to the current window object.
   381  */
   382 function synthesizeKey(aKey, aEvent, aWindow)
   383 {
   384   var utils = _getDOMWindowUtils(aWindow);
   385   if (utils) {
   386     var keyCode = 0, charCode = 0;
   387     if (aKey.indexOf("VK_") == 0) {
   388       keyCode = KeyEvent["DOM_" + aKey];
   389       if (!keyCode) {
   390         throw "Unknown key: " + aKey;
   391       }
   392     } else {
   393       charCode = aKey.charCodeAt(0);
   394       keyCode = _computeKeyCodeFromChar(aKey.charAt(0));
   395     }
   397     var modifiers = _parseModifiers(aEvent);
   399     if (!("type" in aEvent) || !aEvent.type) {
   400       // Send keydown + (optional) keypress + keyup events.
   401       var keyDownDefaultHappened =
   402           utils.sendKeyEvent("keydown", keyCode, 0, modifiers);
   403       if (isKeypressFiredKey(keyCode)) {
   404         utils.sendKeyEvent("keypress", charCode ? 0 : keyCode, charCode,
   405                            modifiers, !keyDownDefaultHappened);
   406       }
   407       utils.sendKeyEvent("keyup", keyCode, 0, modifiers);
   408     } else if (aEvent.type == "keypress") {
   409       // Send standalone keypress event.
   410       utils.sendKeyEvent(aEvent.type, charCode ? 0 : keyCode,
   411                          charCode, modifiers);
   412     } else {
   413       // Send other standalone event than keypress.
   414       utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers);
   415     }
   416   }
   417 }
   419 var _gSeenEvent = false;
   421 /**
   422  * Indicate that an event with an original target of aExpectedTarget and
   423  * a type of aExpectedEvent is expected to be fired, or not expected to
   424  * be fired.
   425  */
   426 function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName)
   427 {
   428   if (!aExpectedTarget || !aExpectedEvent)
   429     return null;
   431   _gSeenEvent = false;
   433   var type = (aExpectedEvent.charAt(0) == "!") ?
   434              aExpectedEvent.substring(1) : aExpectedEvent;
   435   var eventHandler = function(event) {
   436     var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget &&
   437                    event.type == type);
   438     is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : ""));
   439     _gSeenEvent = true;
   440   };
   442   aExpectedTarget.addEventListener(type, eventHandler, false);
   443   return eventHandler;
   444 }
   446 /**
   447  * Check if the event was fired or not. The event handler aEventHandler
   448  * will be removed.
   449  */
   450 function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName)
   451 {
   452   if (aEventHandler) {
   453     var expectEvent = (aExpectedEvent.charAt(0) != "!");
   454     var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1);
   455     aExpectedTarget.removeEventListener(type, aEventHandler, false);
   456     var desc = type + " event";
   457     if (!expectEvent)
   458       desc += " not";
   459     is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired");
   460   }
   462   _gSeenEvent = false;
   463 }
   465 /**
   466  * Similar to synthesizeMouse except that a test is performed to see if an
   467  * event is fired at the right target as a result.
   468  *
   469  * aExpectedTarget - the expected originalTarget of the event.
   470  * aExpectedEvent - the expected type of the event, such as 'select'.
   471  * aTestName - the test name when outputing results
   472  *
   473  * To test that an event is not fired, use an expected type preceded by an
   474  * exclamation mark, such as '!select'. This might be used to test that a
   475  * click on a disabled element doesn't fire certain events for instance.
   476  *
   477  * aWindow is optional, and defaults to the current window object.
   478  */
   479 function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent,
   480                                     aExpectedTarget, aExpectedEvent, aTestName,
   481                                     aWindow)
   482 {
   483   var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
   484   synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
   485   _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
   486 }
   488 /**
   489  * Similar to synthesizeKey except that a test is performed to see if an
   490  * event is fired at the right target as a result.
   491  *
   492  * aExpectedTarget - the expected originalTarget of the event.
   493  * aExpectedEvent - the expected type of the event, such as 'select'.
   494  * aTestName - the test name when outputing results
   495  *
   496  * To test that an event is not fired, use an expected type preceded by an
   497  * exclamation mark, such as '!select'.
   498  *
   499  * aWindow is optional, and defaults to the current window object.
   500  */
   501 function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent,
   502                                   aTestName, aWindow)
   503 {
   504   var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
   505   synthesizeKey(key, aEvent, aWindow);
   506   _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
   507 }
   509 function disableNonTestMouseEvents(aDisable)
   510 {
   511   var domutils = _getDOMWindowUtils();
   512   domutils.disableNonTestMouseEvents(aDisable);
   513 }
   515 function _getDOMWindowUtils(aWindow)
   516 {
   517   if (!aWindow) {
   518     aWindow = window;
   519   }
   521   //TODO: this is assuming we are in chrome space
   522   return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
   523                                getInterface(Components.interfaces.nsIDOMWindowUtils);
   524 }
   526 // Must be synchronized with nsIDOMWindowUtils.
   527 const COMPOSITION_ATTR_RAWINPUT              = 0x02;
   528 const COMPOSITION_ATTR_SELECTEDRAWTEXT       = 0x03;
   529 const COMPOSITION_ATTR_CONVERTEDTEXT         = 0x04;
   530 const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
   532 /**
   533  * Synthesize a composition event.
   534  *
   535  * @param aEvent               The composition event information.  This must
   536  *                             have |type| member.  The value must be
   537  *                             "compositionstart", "compositionend" or
   538  *                             "compositionupdate".
   539  *                             And also this may have |data| and |locale| which
   540  *                             would be used for the value of each property of
   541  *                             the composition event.  Note that the data would
   542  *                             be ignored if the event type were
   543  *                             "compositionstart".
   544  * @param aWindow              Optional (If null, current |window| will be used)
   545  */
   546 function synthesizeComposition(aEvent, aWindow)
   547 {
   548   var utils = _getDOMWindowUtils(aWindow);
   549   if (!utils) {
   550     return;
   551   }
   553   utils.sendCompositionEvent(aEvent.type, aEvent.data ? aEvent.data : "",
   554                              aEvent.locale ? aEvent.locale : "");
   555 }
   556 /**
   557  * Synthesize a text event.
   558  *
   559  * @param aEvent   The text event's information, this has |composition|
   560  *                 and |caret| members.  |composition| has |string| and
   561  *                 |clauses| members.  |clauses| must be array object.  Each
   562  *                 object has |length| and |attr|.  And |caret| has |start| and
   563  *                 |length|.  See the following tree image.
   564  *
   565  *                 aEvent
   566  *                   +-- composition
   567  *                   |     +-- string
   568  *                   |     +-- clauses[]
   569  *                   |           +-- length
   570  *                   |           +-- attr
   571  *                   +-- caret
   572  *                         +-- start
   573  *                         +-- length
   574  *
   575  *                 Set the composition string to |composition.string|.  Set its
   576  *                 clauses information to the |clauses| array.
   577  *
   578  *                 When it's composing, set the each clauses' length to the
   579  *                 |composition.clauses[n].length|.  The sum of the all length
   580  *                 values must be same as the length of |composition.string|.
   581  *                 Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the
   582  *                 |composition.clauses[n].attr|.
   583  *
   584  *                 When it's not composing, set 0 to the
   585  *                 |composition.clauses[0].length| and
   586  *                 |composition.clauses[0].attr|.
   587  *
   588  *                 Set caret position to the |caret.start|. It's offset from
   589  *                 the start of the composition string.  Set caret length to
   590  *                 |caret.length|.  If it's larger than 0, it should be wide
   591  *                 caret.  However, current nsEditor doesn't support wide
   592  *                 caret, therefore, you should always set 0 now.
   593  *
   594  * @param aWindow  Optional (If null, current |window| will be used)
   595  */
   596 function synthesizeText(aEvent, aWindow)
   597 {
   598   var utils = _getDOMWindowUtils(aWindow);
   599   if (!utils) {
   600     return;
   601   }
   603   if (!aEvent.composition || !aEvent.composition.clauses ||
   604       !aEvent.composition.clauses[0]) {
   605     return;
   606   }
   608   var firstClauseLength = aEvent.composition.clauses[0].length;
   609   var firstClauseAttr   = aEvent.composition.clauses[0].attr;
   610   var secondClauseLength = 0;
   611   var secondClauseAttr = 0;
   612   var thirdClauseLength = 0;
   613   var thirdClauseAttr = 0;
   614   if (aEvent.composition.clauses[1]) {
   615     secondClauseLength = aEvent.composition.clauses[1].length;
   616     secondClauseAttr   = aEvent.composition.clauses[1].attr;
   617     if (aEvent.composition.clauses[2]) {
   618       thirdClauseLength = aEvent.composition.clauses[2].length;
   619       thirdClauseAttr   = aEvent.composition.clauses[2].attr;
   620     }
   621   }
   623   var caretStart = -1;
   624   var caretLength = 0;
   625   if (aEvent.caret) {
   626     caretStart = aEvent.caret.start;
   627     caretLength = aEvent.caret.length;
   628   }
   630   utils.sendTextEvent(aEvent.composition.string,
   631                       firstClauseLength, firstClauseAttr,
   632                       secondClauseLength, secondClauseAttr,
   633                       thirdClauseLength, thirdClauseAttr,
   634                       caretStart, caretLength);
   635 }
   637 /**
   638  * Synthesize a query selected text event.
   639  *
   640  * @param aWindow  Optional (If null, current |window| will be used)
   641  * @return         An nsIQueryContentEventResult object.  If this failed,
   642  *                 the result might be null.
   643  */
   644 function synthesizeQuerySelectedText(aWindow)
   645 {
   646   var utils = _getDOMWindowUtils(aWindow);
   647   if (!utils) {
   648     return null;
   649   }
   651   return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0);
   652 }
   654 /**
   655  * Synthesize a selection set event.
   656  *
   657  * @param aOffset  The character offset.  0 means the first character in the
   658  *                 selection root.
   659  * @param aLength  The length of the text.  If the length is too long,
   660  *                 the extra length is ignored.
   661  * @param aReverse If true, the selection is from |aOffset + aLength| to
   662  *                 |aOffset|.  Otherwise, from |aOffset| to |aOffset + aLength|.
   663  * @param aWindow  Optional (If null, current |window| will be used)
   664  * @return         True, if succeeded.  Otherwise false.
   665  */
   666 function synthesizeSelectionSet(aOffset, aLength, aReverse, aWindow)
   667 {
   668   var utils = _getDOMWindowUtils(aWindow);
   669   if (!utils) {
   670     return false;
   671   }
   672   return utils.sendSelectionSetEvent(aOffset, aLength, aReverse);
   673 }

mercurial