michael@0: /** michael@0: * ChromeUtils.js is a set of mochitest utilities that are used to michael@0: * synthesize events in the browser. These are only used by michael@0: * mochitest-chrome and browser-chrome tests. Originally these functions were in michael@0: * EventUtils.js, but when porting to specialPowers, we didn't want michael@0: * to move unnecessary functions. michael@0: * michael@0: */ michael@0: michael@0: const EventUtils = {}; michael@0: const scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]. michael@0: getService(Components.interfaces.mozIJSSubScriptLoader); michael@0: scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils); michael@0: michael@0: /** michael@0: * Synthesize a query text content event. michael@0: * michael@0: * @param aOffset The character offset. 0 means the first character in the michael@0: * selection root. michael@0: * @param aLength The length of getting text. If the length is too long, michael@0: * the extra length is ignored. michael@0: * @param aWindow Optional (If null, current |window| will be used) michael@0: * @return An nsIQueryContentEventResult object. If this failed, michael@0: * the result might be null. michael@0: */ michael@0: function synthesizeQueryTextContent(aOffset, aLength, aWindow) michael@0: { michael@0: var utils = _getDOMWindowUtils(aWindow); michael@0: if (!utils) { michael@0: return nullptr; michael@0: } michael@0: return utils.sendQueryContentEvent(utils.QUERY_TEXT_CONTENT, michael@0: aOffset, aLength, 0, 0, michael@0: QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK); michael@0: } michael@0: michael@0: /** michael@0: * Synthesize a query text rect event. michael@0: * michael@0: * @param aOffset The character offset. 0 means the first character in the michael@0: * selection root. michael@0: * @param aLength The length of the text. If the length is too long, michael@0: * the extra length is ignored. michael@0: * @param aWindow Optional (If null, current |window| will be used) michael@0: * @return An nsIQueryContentEventResult object. If this failed, michael@0: * the result might be null. michael@0: */ michael@0: function synthesizeQueryTextRect(aOffset, aLength, aWindow) michael@0: { michael@0: var utils = _getDOMWindowUtils(aWindow); michael@0: if (!utils) { michael@0: return nullptr; michael@0: } michael@0: return utils.sendQueryContentEvent(utils.QUERY_TEXT_RECT, michael@0: aOffset, aLength, 0, 0, michael@0: QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK); michael@0: } michael@0: michael@0: /** michael@0: * Synthesize a query editor rect event. michael@0: * michael@0: * @param aWindow Optional (If null, current |window| will be used) michael@0: * @return An nsIQueryContentEventResult object. If this failed, michael@0: * the result might be null. michael@0: */ michael@0: function synthesizeQueryEditorRect(aWindow) michael@0: { michael@0: var utils = _getDOMWindowUtils(aWindow); michael@0: if (!utils) { michael@0: return nullptr; michael@0: } michael@0: return utils.sendQueryContentEvent(utils.QUERY_EDITOR_RECT, 0, 0, 0, 0, michael@0: QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK); michael@0: } michael@0: michael@0: /** michael@0: * Synthesize a character at point event. michael@0: * michael@0: * @param aX, aY The offset in the client area of the DOM window. michael@0: * @param aWindow Optional (If null, current |window| will be used) michael@0: * @return An nsIQueryContentEventResult object. If this failed, michael@0: * the result might be null. michael@0: */ michael@0: function synthesizeCharAtPoint(aX, aY, aWindow) michael@0: { michael@0: var utils = _getDOMWindowUtils(aWindow); michael@0: if (!utils) { michael@0: return nullptr; michael@0: } michael@0: return utils.sendQueryContentEvent(utils.QUERY_CHARACTER_AT_POINT, michael@0: 0, 0, aX, aY, michael@0: QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK); michael@0: } michael@0: michael@0: /** michael@0: * Emulate a dragstart event. michael@0: * element - element to fire the dragstart event on michael@0: * expectedDragData - the data you expect the data transfer to contain afterwards michael@0: * This data is in the format: michael@0: * [ [ {type: value, data: value, test: function}, ... ], ... ] michael@0: * can be null michael@0: * aWindow - optional; defaults to the current window object. michael@0: * x - optional; initial x coordinate michael@0: * y - optional; initial y coordinate michael@0: * Returns null if data matches. michael@0: * Returns the event.dataTransfer if data does not match michael@0: * michael@0: * eqTest is an optional function if comparison can't be done with x == y; michael@0: * function (actualData, expectedData) {return boolean} michael@0: * @param actualData from dataTransfer michael@0: * @param expectedData from expectedDragData michael@0: * see bug 462172 for example of use michael@0: * michael@0: */ michael@0: function synthesizeDragStart(element, expectedDragData, aWindow, x, y) michael@0: { michael@0: if (!aWindow) michael@0: aWindow = window; michael@0: x = x || 2; michael@0: y = y || 2; michael@0: const step = 9; michael@0: michael@0: var result = "trapDrag was not called"; michael@0: var trapDrag = function(event) { michael@0: try { michael@0: var dataTransfer = event.dataTransfer; michael@0: result = null; michael@0: if (!dataTransfer) michael@0: throw "no dataTransfer"; michael@0: if (expectedDragData == null || michael@0: dataTransfer.mozItemCount != expectedDragData.length) michael@0: throw dataTransfer; michael@0: for (var i = 0; i < dataTransfer.mozItemCount; i++) { michael@0: var dtTypes = dataTransfer.mozTypesAt(i); michael@0: if (dtTypes.length != expectedDragData[i].length) michael@0: throw dataTransfer; michael@0: for (var j = 0; j < dtTypes.length; j++) { michael@0: if (dtTypes[j] != expectedDragData[i][j].type) michael@0: throw dataTransfer; michael@0: var dtData = dataTransfer.mozGetDataAt(dtTypes[j],i); michael@0: if (expectedDragData[i][j].eqTest) { michael@0: if (!expectedDragData[i][j].eqTest(dtData, expectedDragData[i][j].data)) michael@0: throw dataTransfer; michael@0: } michael@0: else if (expectedDragData[i][j].data != dtData) michael@0: throw dataTransfer; michael@0: } michael@0: } michael@0: } catch(ex) { michael@0: result = ex; michael@0: } michael@0: event.preventDefault(); michael@0: event.stopPropagation(); michael@0: } michael@0: aWindow.addEventListener("dragstart", trapDrag, false); michael@0: EventUtils.synthesizeMouse(element, x, y, { type: "mousedown" }, aWindow); michael@0: x += step; y += step; michael@0: EventUtils.synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow); michael@0: x += step; y += step; michael@0: EventUtils.synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow); michael@0: aWindow.removeEventListener("dragstart", trapDrag, false); michael@0: EventUtils.synthesizeMouse(element, x, y, { type: "mouseup" }, aWindow); michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * Emulate a drop by emulating a dragstart and firing events dragenter, dragover, and drop. michael@0: * srcElement - the element to use to start the drag, usually the same as destElement michael@0: * but if destElement isn't suitable to start a drag on pass a suitable michael@0: * element for srcElement michael@0: * destElement - the element to fire the dragover, dragleave and drop events michael@0: * dragData - the data to supply for the data transfer michael@0: * This data is in the format: michael@0: * [ [ {type: value, data: value}, ...], ... ] michael@0: * dropEffect - the drop effect to set during the dragstart event, or 'move' if null michael@0: * aWindow - optional; defaults to the current window object. michael@0: * aDestWindow - optional; defaults to aWindow. michael@0: * Used when destElement is in a different window than srcElement. michael@0: * michael@0: * Returns the drop effect that was desired. michael@0: */ michael@0: function synthesizeDrop(srcElement, destElement, dragData, dropEffect, aWindow, aDestWindow) michael@0: { michael@0: if (!aWindow) michael@0: aWindow = window; michael@0: if (!aDestWindow) michael@0: aDestWindow = aWindow; michael@0: michael@0: var gWindowUtils = aDestWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor). michael@0: getInterface(Components.interfaces.nsIDOMWindowUtils); michael@0: var ds = Components.classes["@mozilla.org/widget/dragservice;1"]. michael@0: getService(Components.interfaces.nsIDragService); michael@0: michael@0: var dataTransfer; michael@0: var trapDrag = function(event) { michael@0: dataTransfer = event.dataTransfer; michael@0: for (var i = 0; i < dragData.length; i++) { michael@0: var item = dragData[i]; michael@0: for (var j = 0; j < item.length; j++) { michael@0: dataTransfer.mozSetDataAt(item[j].type, item[j].data, i); michael@0: } michael@0: } michael@0: dataTransfer.dropEffect = dropEffect || "move"; michael@0: event.preventDefault(); michael@0: event.stopPropagation(); michael@0: } michael@0: michael@0: ds.startDragSession(); michael@0: michael@0: try { michael@0: // need to use real mouse action michael@0: aWindow.addEventListener("dragstart", trapDrag, true); michael@0: EventUtils.synthesizeMouseAtCenter(srcElement, { type: "mousedown" }, aWindow); michael@0: michael@0: var rect = srcElement.getBoundingClientRect(); michael@0: var x = rect.width / 2; michael@0: var y = rect.height / 2; michael@0: EventUtils.synthesizeMouse(srcElement, x, y, { type: "mousemove" }, aWindow); michael@0: EventUtils.synthesizeMouse(srcElement, x+10, y+10, { type: "mousemove" }, aWindow); michael@0: aWindow.removeEventListener("dragstart", trapDrag, true); michael@0: michael@0: event = aDestWindow.document.createEvent("DragEvents"); michael@0: event.initDragEvent("dragenter", true, true, aDestWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); michael@0: gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true); michael@0: var event = aDestWindow.document.createEvent("DragEvents"); michael@0: event.initDragEvent("dragover", true, true, aDestWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); michael@0: if (gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true)) { michael@0: EventUtils.synthesizeMouseAtCenter(destElement, { type: "mouseup" }, aDestWindow); michael@0: return "none"; michael@0: } michael@0: michael@0: if (dataTransfer.dropEffect != "none") { michael@0: event = aDestWindow.document.createEvent("DragEvents"); michael@0: event.initDragEvent("drop", true, true, aDestWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); michael@0: gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true); michael@0: } michael@0: michael@0: EventUtils.synthesizeMouseAtCenter(destElement, { type: "mouseup" }, aDestWindow); michael@0: michael@0: return dataTransfer.dropEffect; michael@0: } finally { michael@0: ds.endDragSession(true); michael@0: } michael@0: }; michael@0: michael@0: var PluginUtils = michael@0: { michael@0: withTestPlugin : function(callback) michael@0: { michael@0: if (typeof Components == "undefined") michael@0: { michael@0: todo(false, "Not a Mozilla-based browser"); michael@0: return false; michael@0: } michael@0: michael@0: var ph = Components.classes["@mozilla.org/plugin/host;1"] michael@0: .getService(Components.interfaces.nsIPluginHost); michael@0: var tags = ph.getPluginTags(); michael@0: michael@0: // Find the test plugin michael@0: for (var i = 0; i < tags.length; i++) michael@0: { michael@0: if (tags[i].name == "Test Plug-in") michael@0: { michael@0: callback(tags[i]); michael@0: return true; michael@0: } michael@0: } michael@0: todo(false, "Need a test plugin on this platform"); michael@0: return false; michael@0: } michael@0: };