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