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

http://github.com/zpao/v8monkey · JavaScript · 820 lines · 449 code · 72 blank · 299 comment · 104 complexity · ad6f329c296915b072080873b94c9186 MD5 · raw file

  1. // Export all available functions for Mozmill
  2. var EXPORTED_SYMBOLS = ["sendMouseEvent", "sendChar", "sendString", "sendKey",
  3. "synthesizeMouse", "synthesizeMouseScroll", "synthesizeKey",
  4. "synthesizeMouseExpectEvent", "synthesizeKeyExpectEvent",
  5. "synthesizeDragStart", "synthesizeDrop", "synthesizeText",
  6. "disableNonTestMouseEvents", "synthesizeComposition",
  7. "synthesizeQuerySelectedText", "synthesizeQueryTextContent",
  8. "synthesizeQueryCaretRect", "synthesizeQueryTextRect",
  9. "synthesizeQueryEditorRect", "synthesizeCharAtPoint",
  10. "synthesizeSelectionSet"];
  11. /**
  12. * Get the array with available key events
  13. */
  14. function getKeyEvent(aWindow) {
  15. var win = aWindow.wrappedJSObject ? aWindow.wrappedJSObject : aWindow;
  16. return win.KeyEvent;
  17. }
  18. /**
  19. * EventUtils provides some utility methods for creating and sending DOM events.
  20. * Current methods:
  21. * sendMouseEvent
  22. * sendChar
  23. * sendString
  24. * sendKey
  25. */
  26. /**
  27. * Send a mouse event to the node aTarget (aTarget can be an id, or an
  28. * actual node) . The "event" passed in to aEvent is just a JavaScript
  29. * object with the properties set that the real mouse event object should
  30. * have. This includes the type of the mouse event.
  31. * E.g. to send an click event to the node with id 'node' you might do this:
  32. *
  33. * sendMouseEvent({type:'click'}, 'node');
  34. */
  35. function sendMouseEvent(aEvent, aTarget, aWindow) {
  36. if (['click', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) {
  37. throw new Error("sendMouseEvent doesn't know about event type '"+aEvent.type+"'");
  38. }
  39. if (!aWindow) {
  40. aWindow = window;
  41. }
  42. if (!(aTarget instanceof Element)) {
  43. aTarget = aWindow.document.getElementById(aTarget);
  44. }
  45. var event = aWindow.document.createEvent('MouseEvent');
  46. var typeArg = aEvent.type;
  47. var canBubbleArg = true;
  48. var cancelableArg = true;
  49. var viewArg = aWindow;
  50. var detailArg = aEvent.detail || (aEvent.type == 'click' ||
  51. aEvent.type == 'mousedown' ||
  52. aEvent.type == 'mouseup' ? 1 : 0);
  53. var screenXArg = aEvent.screenX || 0;
  54. var screenYArg = aEvent.screenY || 0;
  55. var clientXArg = aEvent.clientX || 0;
  56. var clientYArg = aEvent.clientY || 0;
  57. var ctrlKeyArg = aEvent.ctrlKey || false;
  58. var altKeyArg = aEvent.altKey || false;
  59. var shiftKeyArg = aEvent.shiftKey || false;
  60. var metaKeyArg = aEvent.metaKey || false;
  61. var buttonArg = aEvent.button || 0;
  62. var relatedTargetArg = aEvent.relatedTarget || null;
  63. event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg,
  64. screenXArg, screenYArg, clientXArg, clientYArg,
  65. ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
  66. buttonArg, relatedTargetArg);
  67. aTarget.dispatchEvent(event);
  68. }
  69. /**
  70. * Send the char aChar to the node with id aTarget. If aTarget is not
  71. * provided, use "target". This method handles casing of chars (sends the
  72. * right charcode, and sends a shift key for uppercase chars). No other
  73. * modifiers are handled at this point.
  74. *
  75. * For now this method only works for English letters (lower and upper case)
  76. * and the digits 0-9.
  77. *
  78. * Returns true if the keypress event was accepted (no calls to preventDefault
  79. * or anything like that), false otherwise.
  80. */
  81. function sendChar(aChar, aTarget) {
  82. // DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9.
  83. var hasShift = (aChar == aChar.toUpperCase());
  84. var charCode = aChar.charCodeAt(0);
  85. var keyCode = charCode;
  86. if (!hasShift) {
  87. // For lowercase letters, the keyCode is actually 32 less than the charCode
  88. keyCode -= 0x20;
  89. }
  90. return __doEventDispatch(aTarget, charCode, keyCode, hasShift);
  91. }
  92. /**
  93. * Send the string aStr to the node with id aTarget. If aTarget is not
  94. * provided, use "target".
  95. *
  96. * For now this method only works for English letters (lower and upper case)
  97. * and the digits 0-9.
  98. */
  99. function sendString(aStr, aTarget) {
  100. for (var i = 0; i < aStr.length; ++i) {
  101. sendChar(aStr.charAt(i), aTarget);
  102. }
  103. }
  104. /**
  105. * Send the non-character key aKey to the node with id aTarget. If aTarget is
  106. * not provided, use "target". The name of the key should be a lowercase
  107. * version of the part that comes after "DOM_VK_" in the KeyEvent constant
  108. * name for this key. No modifiers are handled at this point.
  109. *
  110. * Returns true if the keypress event was accepted (no calls to preventDefault
  111. * or anything like that), false otherwise.
  112. */
  113. function sendKey(aKey, aTarget, aWindow) {
  114. if (!aWindow)
  115. aWindow = window;
  116. keyName = "DOM_VK_" + aKey.toUpperCase();
  117. if (!getKeyEvent(aWindow)[keyName]) {
  118. throw "Unknown key: " + keyName;
  119. }
  120. return __doEventDispatch(aTarget, 0, getKeyEvent(aWindow)[keyName], false);
  121. }
  122. /**
  123. * Actually perform event dispatch given a charCode, keyCode, and boolean for
  124. * whether "shift" was pressed. Send the event to the node with id aTarget. If
  125. * aTarget is not provided, use "target".
  126. *
  127. * Returns true if the keypress event was accepted (no calls to preventDefault
  128. * or anything like that), false otherwise.
  129. */
  130. function __doEventDispatch(aTarget, aCharCode, aKeyCode, aHasShift) {
  131. if (aTarget === undefined) {
  132. aTarget = "target";
  133. }
  134. var event = document.createEvent("KeyEvents");
  135. event.initKeyEvent("keydown", true, true, document.defaultView,
  136. false, false, aHasShift, false,
  137. aKeyCode, 0);
  138. var accepted = $(aTarget).dispatchEvent(event);
  139. // Preventing the default keydown action also prevents the default
  140. // keypress action.
  141. event = document.createEvent("KeyEvents");
  142. if (aCharCode) {
  143. event.initKeyEvent("keypress", true, true, document.defaultView,
  144. false, false, aHasShift, false,
  145. 0, aCharCode);
  146. } else {
  147. event.initKeyEvent("keypress", true, true, document.defaultView,
  148. false, false, aHasShift, false,
  149. aKeyCode, 0);
  150. }
  151. if (!accepted) {
  152. event.preventDefault();
  153. }
  154. accepted = $(aTarget).dispatchEvent(event);
  155. // Always send keyup
  156. var event = document.createEvent("KeyEvents");
  157. event.initKeyEvent("keyup", true, true, document.defaultView,
  158. false, false, aHasShift, false,
  159. aKeyCode, 0);
  160. $(aTarget).dispatchEvent(event);
  161. return accepted;
  162. }
  163. /**
  164. * Parse the key modifier flags from aEvent. Used to share code between
  165. * synthesizeMouse and synthesizeKey.
  166. */
  167. function _parseModifiers(aEvent)
  168. {
  169. var hwindow = Components.classes["@mozilla.org/appshell/appShellService;1"]
  170. .getService(Components.interfaces.nsIAppShellService)
  171. .hiddenDOMWindow;
  172. const masks = Components.interfaces.nsIDOMNSEvent;
  173. var mval = 0;
  174. if (aEvent.shiftKey)
  175. mval |= masks.SHIFT_MASK;
  176. if (aEvent.ctrlKey)
  177. mval |= masks.CONTROL_MASK;
  178. if (aEvent.altKey)
  179. mval |= masks.ALT_MASK;
  180. if (aEvent.metaKey)
  181. mval |= masks.META_MASK;
  182. if (aEvent.accelKey)
  183. mval |= (hwindow.navigator.platform.indexOf("Mac") >= 0) ? masks.META_MASK :
  184. masks.CONTROL_MASK;
  185. return mval;
  186. }
  187. /**
  188. * Synthesize a mouse event on a target. The actual client point is determined
  189. * by taking the aTarget's client box and offseting it by aOffsetX and
  190. * aOffsetY. This allows mouse clicks to be simulated by calling this method.
  191. *
  192. * aEvent is an object which may contain the properties:
  193. * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
  194. *
  195. * If the type is specified, an mouse event of that type is fired. Otherwise,
  196. * a mousedown followed by a mouse up is performed.
  197. *
  198. * aWindow is optional, and defaults to the current window object.
  199. */
  200. function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
  201. {
  202. if (!aWindow)
  203. aWindow = window;
  204. var utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  205. getInterface(Components.interfaces.nsIDOMWindowUtils);
  206. if (utils) {
  207. var button = aEvent.button || 0;
  208. var clickCount = aEvent.clickCount || 1;
  209. var modifiers = _parseModifiers(aEvent);
  210. var rect = aTarget.getBoundingClientRect();
  211. var left = rect.left + aOffsetX;
  212. var top = rect.top + aOffsetY;
  213. if (aEvent.type) {
  214. utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
  215. }
  216. else {
  217. utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers);
  218. utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers);
  219. }
  220. }
  221. }
  222. /**
  223. * Synthesize a mouse scroll event on a target. The actual client point is determined
  224. * by taking the aTarget's client box and offseting it by aOffsetX and
  225. * aOffsetY.
  226. *
  227. * aEvent is an object which may contain the properties:
  228. * shiftKey, ctrlKey, altKey, metaKey, accessKey, button, type, axis, delta, hasPixels
  229. *
  230. * If the type is specified, a mouse scroll event of that type is fired. Otherwise,
  231. * "DOMMouseScroll" is used.
  232. *
  233. * If the axis is specified, it must be one of "horizontal" or "vertical". If not specified,
  234. * "vertical" is used.
  235. *
  236. * 'delta' is the amount to scroll by (can be positive or negative). It must
  237. * be specified.
  238. *
  239. * 'hasPixels' specifies whether kHasPixels should be set in the scrollFlags.
  240. *
  241. * aWindow is optional, and defaults to the current window object.
  242. */
  243. function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
  244. {
  245. if (!aWindow)
  246. aWindow = window;
  247. var utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  248. getInterface(Components.interfaces.nsIDOMWindowUtils);
  249. if (utils) {
  250. // See nsMouseScrollFlags in nsGUIEvent.h
  251. const kIsVertical = 0x02;
  252. const kIsHorizontal = 0x04;
  253. const kHasPixels = 0x08;
  254. var button = aEvent.button || 0;
  255. var modifiers = _parseModifiers(aEvent);
  256. var rect = aTarget.getBoundingClientRect();
  257. var left = rect.left;
  258. var top = rect.top;
  259. var type = aEvent.type || "DOMMouseScroll";
  260. var axis = aEvent.axis || "vertical";
  261. var scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical;
  262. if (aEvent.hasPixels) {
  263. scrollFlags |= kHasPixels;
  264. }
  265. utils.sendMouseScrollEvent(type, left + aOffsetX, top + aOffsetY, button,
  266. scrollFlags, aEvent.delta, modifiers);
  267. }
  268. }
  269. /**
  270. * Synthesize a key event. It is targeted at whatever would be targeted by an
  271. * actual keypress by the user, typically the focused element.
  272. *
  273. * aKey should be either a character or a keycode starting with VK_ such as
  274. * VK_ENTER.
  275. *
  276. * aEvent is an object which may contain the properties:
  277. * shiftKey, ctrlKey, altKey, metaKey, accessKey, type
  278. *
  279. * If the type is specified, a key event of that type is fired. Otherwise,
  280. * a keydown, a keypress and then a keyup event are fired in sequence.
  281. *
  282. * aWindow is optional, and defaults to the current window object.
  283. */
  284. function synthesizeKey(aKey, aEvent, aWindow)
  285. {
  286. if (!aWindow)
  287. aWindow = window;
  288. var utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  289. getInterface(Components.interfaces.nsIDOMWindowUtils);
  290. if (utils) {
  291. var keyCode = 0, charCode = 0;
  292. if (aKey.indexOf("VK_") == 0)
  293. keyCode = getKeyEvent(aWindow)["DOM_" + aKey];
  294. else
  295. charCode = aKey.charCodeAt(0);
  296. var modifiers = _parseModifiers(aEvent);
  297. if (aEvent.type) {
  298. utils.sendKeyEvent(aEvent.type, keyCode, charCode, modifiers);
  299. }
  300. else {
  301. var keyDownDefaultHappened =
  302. utils.sendKeyEvent("keydown", keyCode, charCode, modifiers);
  303. utils.sendKeyEvent("keypress", keyCode, charCode, modifiers,
  304. !keyDownDefaultHappened);
  305. utils.sendKeyEvent("keyup", keyCode, charCode, modifiers);
  306. }
  307. }
  308. }
  309. var _gSeenEvent = false;
  310. /**
  311. * Indicate that an event with an original target of aExpectedTarget and
  312. * a type of aExpectedEvent is expected to be fired, or not expected to
  313. * be fired.
  314. */
  315. function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName)
  316. {
  317. if (!aExpectedTarget || !aExpectedEvent)
  318. return null;
  319. _gSeenEvent = false;
  320. var type = (aExpectedEvent.charAt(0) == "!") ?
  321. aExpectedEvent.substring(1) : aExpectedEvent;
  322. var eventHandler = function(event) {
  323. var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget &&
  324. event.type == type);
  325. if (!epassed)
  326. throw new Error(aTestName + " " + type + " event target " +
  327. (_gSeenEvent ? "twice" : ""));
  328. _gSeenEvent = true;
  329. };
  330. aExpectedTarget.addEventListener(type, eventHandler, false);
  331. return eventHandler;
  332. }
  333. /**
  334. * Check if the event was fired or not. The event handler aEventHandler
  335. * will be removed.
  336. */
  337. function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName)
  338. {
  339. if (aEventHandler) {
  340. var expectEvent = (aExpectedEvent.charAt(0) != "!");
  341. var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1);
  342. aExpectedTarget.removeEventListener(type, aEventHandler, false);
  343. var desc = type + " event";
  344. if (expectEvent)
  345. desc += " not";
  346. if (_gSeenEvent != expectEvent)
  347. throw new Error(aTestName + ": " + desc + " fired.");
  348. }
  349. _gSeenEvent = false;
  350. }
  351. /**
  352. * Similar to synthesizeMouse except that a test is performed to see if an
  353. * event is fired at the right target as a result.
  354. *
  355. * aExpectedTarget - the expected originalTarget of the event.
  356. * aExpectedEvent - the expected type of the event, such as 'select'.
  357. * aTestName - the test name when outputing results
  358. *
  359. * To test that an event is not fired, use an expected type preceded by an
  360. * exclamation mark, such as '!select'. This might be used to test that a
  361. * click on a disabled element doesn't fire certain events for instance.
  362. *
  363. * aWindow is optional, and defaults to the current window object.
  364. */
  365. function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent,
  366. aExpectedTarget, aExpectedEvent, aTestName,
  367. aWindow)
  368. {
  369. var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
  370. synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
  371. _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
  372. }
  373. /**
  374. * Similar to synthesizeKey except that a test is performed to see if an
  375. * event is fired at the right target as a result.
  376. *
  377. * aExpectedTarget - the expected originalTarget of the event.
  378. * aExpectedEvent - the expected type of the event, such as 'select'.
  379. * aTestName - the test name when outputing results
  380. *
  381. * To test that an event is not fired, use an expected type preceded by an
  382. * exclamation mark, such as '!select'.
  383. *
  384. * aWindow is optional, and defaults to the current window object.
  385. */
  386. function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent,
  387. aTestName, aWindow)
  388. {
  389. var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
  390. synthesizeKey(key, aEvent, aWindow);
  391. _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
  392. }
  393. /**
  394. * Emulate a dragstart event.
  395. * element - element to fire the dragstart event on
  396. * expectedDragData - the data you expect the data transfer to contain afterwards
  397. * This data is in the format:
  398. * [ [ {type: value, data: value, test: function}, ... ], ... ]
  399. * can be null
  400. * aWindow - optional; defaults to the current window object.
  401. * x - optional; initial x coordinate
  402. * y - optional; initial y coordinate
  403. * Returns null if data matches.
  404. * Returns the event.dataTransfer if data does not match
  405. *
  406. * eqTest is an optional function if comparison can't be done with x == y;
  407. * function (actualData, expectedData) {return boolean}
  408. * @param actualData from dataTransfer
  409. * @param expectedData from expectedDragData
  410. * see bug 462172 for example of use
  411. *
  412. */
  413. function synthesizeDragStart(element, expectedDragData, aWindow, x, y)
  414. {
  415. if (!aWindow)
  416. aWindow = window;
  417. x = x || 2;
  418. y = y || 2;
  419. const step = 9;
  420. var result = "trapDrag was not called";
  421. var trapDrag = function(event) {
  422. try {
  423. var dataTransfer = event.dataTransfer;
  424. result = null;
  425. if (!dataTransfer)
  426. throw "no dataTransfer";
  427. if (expectedDragData == null ||
  428. dataTransfer.mozItemCount != expectedDragData.length)
  429. throw dataTransfer;
  430. for (var i = 0; i < dataTransfer.mozItemCount; i++) {
  431. var dtTypes = dataTransfer.mozTypesAt(i);
  432. if (dtTypes.length != expectedDragData[i].length)
  433. throw dataTransfer;
  434. for (var j = 0; j < dtTypes.length; j++) {
  435. if (dtTypes[j] != expectedDragData[i][j].type)
  436. throw dataTransfer;
  437. var dtData = dataTransfer.mozGetDataAt(dtTypes[j],i);
  438. if (expectedDragData[i][j].eqTest) {
  439. if (!expectedDragData[i][j].eqTest(dtData, expectedDragData[i][j].data))
  440. throw dataTransfer;
  441. }
  442. else if (expectedDragData[i][j].data != dtData)
  443. throw dataTransfer;
  444. }
  445. }
  446. } catch(ex) {
  447. result = ex;
  448. }
  449. event.preventDefault();
  450. event.stopPropagation();
  451. }
  452. aWindow.addEventListener("dragstart", trapDrag, false);
  453. synthesizeMouse(element, x, y, { type: "mousedown" }, aWindow);
  454. x += step; y += step;
  455. synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow);
  456. x += step; y += step;
  457. synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow);
  458. aWindow.removeEventListener("dragstart", trapDrag, false);
  459. synthesizeMouse(element, x, y, { type: "mouseup" }, aWindow);
  460. return result;
  461. }
  462. /**
  463. * Emulate a drop by emulating a dragstart and firing events dragenter, dragover, and drop.
  464. * srcElement - the element to use to start the drag, usually the same as destElement
  465. * but if destElement isn't suitable to start a drag on pass a suitable
  466. * element for srcElement
  467. * destElement - the element to fire the dragover, dragleave and drop events
  468. * dragData - the data to supply for the data transfer
  469. * This data is in the format:
  470. * [ [ {type: value, data: value}, ...], ... ]
  471. * dropEffect - the drop effect to set during the dragstart event, or 'move' if null
  472. * aWindow - optional; defaults to the current window object.
  473. *
  474. * Returns the drop effect that was desired.
  475. */
  476. function synthesizeDrop(srcElement, destElement, dragData, dropEffect, aWindow)
  477. {
  478. if (!aWindow)
  479. aWindow = window;
  480. var dataTransfer;
  481. var trapDrag = function(event) {
  482. dataTransfer = event.dataTransfer;
  483. for (var i = 0; i < dragData.length; i++) {
  484. var item = dragData[i];
  485. for (var j = 0; j < item.length; j++) {
  486. dataTransfer.mozSetDataAt(item[j].type, item[j].data, i);
  487. }
  488. }
  489. dataTransfer.dropEffect = dropEffect || "move";
  490. event.preventDefault();
  491. event.stopPropagation();
  492. }
  493. // need to use real mouse action
  494. aWindow.addEventListener("dragstart", trapDrag, true);
  495. synthesizeMouse(srcElement, 2, 2, { type: "mousedown" }, aWindow);
  496. synthesizeMouse(srcElement, 11, 11, { type: "mousemove" }, aWindow);
  497. synthesizeMouse(srcElement, 20, 20, { type: "mousemove" }, aWindow);
  498. aWindow.removeEventListener("dragstart", trapDrag, true);
  499. event = aWindow.document.createEvent("DragEvents");
  500. event.initDragEvent("dragenter", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
  501. destElement.dispatchEvent(event);
  502. var event = aWindow.document.createEvent("DragEvents");
  503. event.initDragEvent("dragover", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
  504. if (destElement.dispatchEvent(event)) {
  505. synthesizeMouse(destElement, 20, 20, { type: "mouseup" }, aWindow);
  506. return "none";
  507. }
  508. if (dataTransfer.dropEffect != "none") {
  509. event = aWindow.document.createEvent("DragEvents");
  510. event.initDragEvent("drop", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
  511. destElement.dispatchEvent(event);
  512. }
  513. synthesizeMouse(destElement, 20, 20, { type: "mouseup" }, aWindow);
  514. return dataTransfer.dropEffect;
  515. }
  516. function disableNonTestMouseEvents(aDisable)
  517. {
  518. var utils =
  519. window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  520. getInterface(Components.interfaces.nsIDOMWindowUtils);
  521. if (utils)
  522. utils.disableNonTestMouseEvents(aDisable);
  523. }
  524. function _getDOMWindowUtils(aWindow)
  525. {
  526. if (!aWindow) {
  527. aWindow = window;
  528. }
  529. return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
  530. getInterface(Components.interfaces.nsIDOMWindowUtils);
  531. }
  532. /**
  533. * Synthesize a composition event.
  534. *
  535. * @param aIsCompositionStart If true, this synthesize compositionstart event.
  536. * Otherwise, compositionend event.
  537. * @param aWindow Optional (If null, current |window| will be used)
  538. */
  539. function synthesizeComposition(aIsCompositionStart, aWindow)
  540. {
  541. var utils = _getDOMWindowUtils(aWindow);
  542. if (!utils) {
  543. return;
  544. }
  545. utils.sendCompositionEvent(aIsCompositionStart ?
  546. "compositionstart" : "compositionend");
  547. }
  548. /**
  549. * Synthesize a text event.
  550. *
  551. * @param aEvent The text event's information, this has |composition|
  552. * and |caret| members. |composition| has |string| and
  553. * |clauses| members. |clauses| must be array object. Each
  554. * object has |length| and |attr|. And |caret| has |start| and
  555. * |length|. See the following tree image.
  556. *
  557. * aEvent
  558. * +-- composition
  559. * | +-- string
  560. * | +-- clauses[]
  561. * | +-- length
  562. * | +-- attr
  563. * +-- caret
  564. * +-- start
  565. * +-- length
  566. *
  567. * Set the composition string to |composition.string|. Set its
  568. * clauses information to the |clauses| array.
  569. *
  570. * When it's composing, set the each clauses' length to the
  571. * |composition.clauses[n].length|. The sum of the all length
  572. * values must be same as the length of |composition.string|.
  573. * Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the
  574. * |composition.clauses[n].attr|.
  575. *
  576. * When it's not composing, set 0 to the
  577. * |composition.clauses[0].length| and
  578. * |composition.clauses[0].attr|.
  579. *
  580. * Set caret position to the |caret.start|. It's offset from
  581. * the start of the composition string. Set caret length to
  582. * |caret.length|. If it's larger than 0, it should be wide
  583. * caret. However, current nsEditor doesn't support wide
  584. * caret, therefore, you should always set 0 now.
  585. *
  586. * @param aWindow Optional (If null, current |window| will be used)
  587. */
  588. function synthesizeText(aEvent, aWindow)
  589. {
  590. var utils = _getDOMWindowUtils(aWindow);
  591. if (!utils) {
  592. return;
  593. }
  594. if (!aEvent.composition || !aEvent.composition.clauses ||
  595. !aEvent.composition.clauses[0]) {
  596. return;
  597. }
  598. var firstClauseLength = aEvent.composition.clauses[0].length;
  599. var firstClauseAttr = aEvent.composition.clauses[0].attr;
  600. var secondClauseLength = 0;
  601. var secondClauseAttr = 0;
  602. var thirdClauseLength = 0;
  603. var thirdClauseAttr = 0;
  604. if (aEvent.composition.clauses[1]) {
  605. secondClauseLength = aEvent.composition.clauses[1].length;
  606. secondClauseAttr = aEvent.composition.clauses[1].attr;
  607. if (aEvent.composition.clauses[2]) {
  608. thirdClauseLength = aEvent.composition.clauses[2].length;
  609. thirdClauseAttr = aEvent.composition.clauses[2].attr;
  610. }
  611. }
  612. var caretStart = -1;
  613. var caretLength = 0;
  614. if (aEvent.caret) {
  615. caretStart = aEvent.caret.start;
  616. caretLength = aEvent.caret.length;
  617. }
  618. utils.sendTextEvent(aEvent.composition.string,
  619. firstClauseLength, firstClauseAttr,
  620. secondClauseLength, secondClauseAttr,
  621. thirdClauseLength, thirdClauseAttr,
  622. caretStart, caretLength);
  623. }
  624. /**
  625. * Synthesize a query selected text event.
  626. *
  627. * @param aWindow Optional (If null, current |window| will be used)
  628. * @return An nsIQueryContentEventResult object. If this failed,
  629. * the result might be null.
  630. */
  631. function synthesizeQuerySelectedText(aWindow)
  632. {
  633. var utils = _getDOMWindowUtils(aWindow);
  634. if (!utils) {
  635. return nsnull;
  636. }
  637. return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0);
  638. }
  639. /**
  640. * Synthesize a query text content event.
  641. *
  642. * @param aOffset The character offset. 0 means the first character in the
  643. * selection root.
  644. * @param aLength The length of getting text. If the length is too long,
  645. * the extra length is ignored.
  646. * @param aWindow Optional (If null, current |window| will be used)
  647. * @return An nsIQueryContentEventResult object. If this failed,
  648. * the result might be null.
  649. */
  650. function synthesizeQueryTextContent(aOffset, aLength, aWindow)
  651. {
  652. var utils = _getDOMWindowUtils(aWindow);
  653. if (!utils) {
  654. return nsnull;
  655. }
  656. return utils.sendQueryContentEvent(utils.QUERY_TEXT_CONTENT,
  657. aOffset, aLength, 0, 0);
  658. }
  659. /**
  660. * Synthesize a query caret rect event.
  661. *
  662. * @param aOffset The caret offset. 0 means left side of the first character
  663. * in the selection root.
  664. * @param aWindow Optional (If null, current |window| will be used)
  665. * @return An nsIQueryContentEventResult object. If this failed,
  666. * the result might be null.
  667. */
  668. function synthesizeQueryCaretRect(aOffset, aWindow)
  669. {
  670. var utils = _getDOMWindowUtils(aWindow);
  671. if (!utils) {
  672. return nsnull;
  673. }
  674. return utils.sendQueryContentEvent(utils.QUERY_CARET_RECT,
  675. aOffset, 0, 0, 0);
  676. }
  677. /**
  678. * Synthesize a query text rect event.
  679. *
  680. * @param aOffset The character offset. 0 means the first character in the
  681. * selection root.
  682. * @param aLength The length of the text. If the length is too long,
  683. * the extra length is ignored.
  684. * @param aWindow Optional (If null, current |window| will be used)
  685. * @return An nsIQueryContentEventResult object. If this failed,
  686. * the result might be null.
  687. */
  688. function synthesizeQueryTextRect(aOffset, aLength, aWindow)
  689. {
  690. var utils = _getDOMWindowUtils(aWindow);
  691. if (!utils) {
  692. return nsnull;
  693. }
  694. return utils.sendQueryContentEvent(utils.QUERY_TEXT_RECT,
  695. aOffset, aLength, 0, 0);
  696. }
  697. /**
  698. * Synthesize a query editor rect event.
  699. *
  700. * @param aWindow Optional (If null, current |window| will be used)
  701. * @return An nsIQueryContentEventResult object. If this failed,
  702. * the result might be null.
  703. */
  704. function synthesizeQueryEditorRect(aWindow)
  705. {
  706. var utils = _getDOMWindowUtils(aWindow);
  707. if (!utils) {
  708. return nsnull;
  709. }
  710. return utils.sendQueryContentEvent(utils.QUERY_EDITOR_RECT, 0, 0, 0, 0);
  711. }
  712. /**
  713. * Synthesize a character at point event.
  714. *
  715. * @param aX, aY The offset in the client area of the DOM window.
  716. * @param aWindow Optional (If null, current |window| will be used)
  717. * @return An nsIQueryContentEventResult object. If this failed,
  718. * the result might be null.
  719. */
  720. function synthesizeCharAtPoint(aX, aY, aWindow)
  721. {
  722. var utils = _getDOMWindowUtils(aWindow);
  723. if (!utils) {
  724. return nsnull;
  725. }
  726. return utils.sendQueryContentEvent(utils.QUERY_CHARACTER_AT_POINT,
  727. 0, 0, aX, aY);
  728. }
  729. /**
  730. * Synthesize a selection set event.
  731. *
  732. * @param aOffset The character offset. 0 means the first character in the
  733. * selection root.
  734. * @param aLength The length of the text. If the length is too long,
  735. * the extra length is ignored.
  736. * @param aReverse If true, the selection is from |aOffset + aLength| to
  737. * |aOffset|. Otherwise, from |aOffset| to |aOffset + aLength|.
  738. * @param aWindow Optional (If null, current |window| will be used)
  739. * @return True, if succeeded. Otherwise false.
  740. */
  741. function synthesizeSelectionSet(aOffset, aLength, aReverse, aWindow)
  742. {
  743. var utils = _getDOMWindowUtils(aWindow);
  744. if (!utils) {
  745. return false;
  746. }
  747. return utils.sendSelectionSetEvent(aOffset, aLength, aReverse);
  748. }