1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/marionette/ChromeUtils.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,291 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/** 1.9 + * ChromeUtils.js is a set of mochitest utilities that are used to 1.10 + * synthesize events in the browser. These are only used by 1.11 + * mochitest-chrome and browser-chrome tests. Originally these functions were in 1.12 + * EventUtils.js, but when porting to specialPowers, we didn't want 1.13 + * to move unnecessary functions. 1.14 + * 1.15 + * ChromeUtils.js depends on EventUtils.js being loaded. 1.16 + * 1.17 + */ 1.18 + 1.19 +/** 1.20 + * Synthesize a query text content event. 1.21 + * 1.22 + * @param aOffset The character offset. 0 means the first character in the 1.23 + * selection root. 1.24 + * @param aLength The length of getting text. If the length is too long, 1.25 + * the extra length is ignored. 1.26 + * @param aWindow Optional (If null, current |window| will be used) 1.27 + * @return An nsIQueryContentEventResult object. If this failed, 1.28 + * the result might be null. 1.29 + */ 1.30 +function synthesizeQueryTextContent(aOffset, aLength, aWindow) 1.31 +{ 1.32 + var utils = _getDOMWindowUtils(aWindow); 1.33 + if (!utils) { 1.34 + return nullptr; 1.35 + } 1.36 + return utils.sendQueryContentEvent(utils.QUERY_TEXT_CONTENT, 1.37 + aOffset, aLength, 0, 0); 1.38 +} 1.39 + 1.40 +/** 1.41 + * Synthesize a query caret rect event. 1.42 + * 1.43 + * @param aOffset The caret offset. 0 means left side of the first character 1.44 + * in the selection root. 1.45 + * @param aWindow Optional (If null, current |window| will be used) 1.46 + * @return An nsIQueryContentEventResult object. If this failed, 1.47 + * the result might be null. 1.48 + */ 1.49 +function synthesizeQueryCaretRect(aOffset, aWindow) 1.50 +{ 1.51 + var utils = _getDOMWindowUtils(aWindow); 1.52 + if (!utils) { 1.53 + return nullptr; 1.54 + } 1.55 + return utils.sendQueryContentEvent(utils.QUERY_CARET_RECT, 1.56 + aOffset, 0, 0, 0); 1.57 +} 1.58 + 1.59 +/** 1.60 + * Synthesize a query text rect event. 1.61 + * 1.62 + * @param aOffset The character offset. 0 means the first character in the 1.63 + * selection root. 1.64 + * @param aLength The length of the text. If the length is too long, 1.65 + * the extra length is ignored. 1.66 + * @param aWindow Optional (If null, current |window| will be used) 1.67 + * @return An nsIQueryContentEventResult object. If this failed, 1.68 + * the result might be null. 1.69 + */ 1.70 +function synthesizeQueryTextRect(aOffset, aLength, aWindow) 1.71 +{ 1.72 + var utils = _getDOMWindowUtils(aWindow); 1.73 + if (!utils) { 1.74 + return nullptr; 1.75 + } 1.76 + return utils.sendQueryContentEvent(utils.QUERY_TEXT_RECT, 1.77 + aOffset, aLength, 0, 0); 1.78 +} 1.79 + 1.80 +/** 1.81 + * Synthesize a query editor rect event. 1.82 + * 1.83 + * @param aWindow Optional (If null, current |window| will be used) 1.84 + * @return An nsIQueryContentEventResult object. If this failed, 1.85 + * the result might be null. 1.86 + */ 1.87 +function synthesizeQueryEditorRect(aWindow) 1.88 +{ 1.89 + var utils = _getDOMWindowUtils(aWindow); 1.90 + if (!utils) { 1.91 + return nullptr; 1.92 + } 1.93 + return utils.sendQueryContentEvent(utils.QUERY_EDITOR_RECT, 0, 0, 0, 0); 1.94 +} 1.95 + 1.96 +/** 1.97 + * Synthesize a character at point event. 1.98 + * 1.99 + * @param aX, aY The offset in the client area of the DOM window. 1.100 + * @param aWindow Optional (If null, current |window| will be used) 1.101 + * @return An nsIQueryContentEventResult object. If this failed, 1.102 + * the result might be null. 1.103 + */ 1.104 +function synthesizeCharAtPoint(aX, aY, aWindow) 1.105 +{ 1.106 + var utils = _getDOMWindowUtils(aWindow); 1.107 + if (!utils) { 1.108 + return nullptr; 1.109 + } 1.110 + return utils.sendQueryContentEvent(utils.QUERY_CHARACTER_AT_POINT, 1.111 + 0, 0, aX, aY); 1.112 +} 1.113 + 1.114 +/** 1.115 + * Emulate a dragstart event. 1.116 + * element - element to fire the dragstart event on 1.117 + * expectedDragData - the data you expect the data transfer to contain afterwards 1.118 + * This data is in the format: 1.119 + * [ [ {type: value, data: value, test: function}, ... ], ... ] 1.120 + * can be null 1.121 + * aWindow - optional; defaults to the current window object. 1.122 + * x - optional; initial x coordinate 1.123 + * y - optional; initial y coordinate 1.124 + * Returns null if data matches. 1.125 + * Returns the event.dataTransfer if data does not match 1.126 + * 1.127 + * eqTest is an optional function if comparison can't be done with x == y; 1.128 + * function (actualData, expectedData) {return boolean} 1.129 + * @param actualData from dataTransfer 1.130 + * @param expectedData from expectedDragData 1.131 + * see bug 462172 for example of use 1.132 + * 1.133 + */ 1.134 +function synthesizeDragStart(element, expectedDragData, aWindow, x, y) 1.135 +{ 1.136 + if (!aWindow) 1.137 + aWindow = window; 1.138 + x = x || 2; 1.139 + y = y || 2; 1.140 + const step = 9; 1.141 + 1.142 + var result = "trapDrag was not called"; 1.143 + var trapDrag = function(event) { 1.144 + try { 1.145 + var dataTransfer = event.dataTransfer; 1.146 + result = null; 1.147 + if (!dataTransfer) 1.148 + throw "no dataTransfer"; 1.149 + if (expectedDragData == null || 1.150 + dataTransfer.mozItemCount != expectedDragData.length) 1.151 + throw dataTransfer; 1.152 + for (var i = 0; i < dataTransfer.mozItemCount; i++) { 1.153 + var dtTypes = dataTransfer.mozTypesAt(i); 1.154 + if (dtTypes.length != expectedDragData[i].length) 1.155 + throw dataTransfer; 1.156 + for (var j = 0; j < dtTypes.length; j++) { 1.157 + if (dtTypes[j] != expectedDragData[i][j].type) 1.158 + throw dataTransfer; 1.159 + var dtData = dataTransfer.mozGetDataAt(dtTypes[j],i); 1.160 + if (expectedDragData[i][j].eqTest) { 1.161 + if (!expectedDragData[i][j].eqTest(dtData, expectedDragData[i][j].data)) 1.162 + throw dataTransfer; 1.163 + } 1.164 + else if (expectedDragData[i][j].data != dtData) 1.165 + throw dataTransfer; 1.166 + } 1.167 + } 1.168 + } catch(ex) { 1.169 + result = ex; 1.170 + } 1.171 + event.preventDefault(); 1.172 + event.stopPropagation(); 1.173 + } 1.174 + aWindow.addEventListener("dragstart", trapDrag, false); 1.175 + synthesizeMouse(element, x, y, { type: "mousedown" }, aWindow); 1.176 + x += step; y += step; 1.177 + synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow); 1.178 + x += step; y += step; 1.179 + synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow); 1.180 + aWindow.removeEventListener("dragstart", trapDrag, false); 1.181 + synthesizeMouse(element, x, y, { type: "mouseup" }, aWindow); 1.182 + return result; 1.183 +} 1.184 + 1.185 +/** 1.186 + * Emulate a drop by emulating a dragstart and firing events dragenter, dragover, and drop. 1.187 + * srcElement - the element to use to start the drag, usually the same as destElement 1.188 + * but if destElement isn't suitable to start a drag on pass a suitable 1.189 + * element for srcElement 1.190 + * destElement - the element to fire the dragover, dragleave and drop events 1.191 + * dragData - the data to supply for the data transfer 1.192 + * This data is in the format: 1.193 + * [ [ {type: value, data: value}, ...], ... ] 1.194 + * dropEffect - the drop effect to set during the dragstart event, or 'move' if null 1.195 + * aWindow - optional; defaults to the current window object. 1.196 + * eventUtils - optional; allows you to pass in a reference to EventUtils.js. 1.197 + * If the eventUtils parameter is not passed in, we assume EventUtils.js is 1.198 + * in the scope. Used by browser-chrome tests. 1.199 + * 1.200 + * Returns the drop effect that was desired. 1.201 + */ 1.202 +function synthesizeDrop(srcElement, destElement, dragData, dropEffect, aWindow, eventUtils) 1.203 +{ 1.204 + if (!aWindow) 1.205 + aWindow = window; 1.206 + 1.207 + var synthesizeMouseAtCenter = (eventUtils || window).synthesizeMouseAtCenter; 1.208 + var synthesizeMouse = (eventUtils || window).synthesizeMouse; 1.209 + 1.210 + var gWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor). 1.211 + getInterface(Components.interfaces.nsIDOMWindowUtils); 1.212 + var ds = Components.classes["@mozilla.org/widget/dragservice;1"]. 1.213 + getService(Components.interfaces.nsIDragService); 1.214 + 1.215 + var dataTransfer; 1.216 + var trapDrag = function(event) { 1.217 + dataTransfer = event.dataTransfer; 1.218 + for (var i = 0; i < dragData.length; i++) { 1.219 + var item = dragData[i]; 1.220 + for (var j = 0; j < item.length; j++) { 1.221 + dataTransfer.mozSetDataAt(item[j].type, item[j].data, i); 1.222 + } 1.223 + } 1.224 + dataTransfer.dropEffect = dropEffect || "move"; 1.225 + event.preventDefault(); 1.226 + event.stopPropagation(); 1.227 + } 1.228 + 1.229 + ds.startDragSession(); 1.230 + 1.231 + try { 1.232 + // need to use real mouse action 1.233 + aWindow.addEventListener("dragstart", trapDrag, true); 1.234 + synthesizeMouseAtCenter(srcElement, { type: "mousedown" }, aWindow); 1.235 + 1.236 + var rect = srcElement.getBoundingClientRect(); 1.237 + var x = rect.width / 2; 1.238 + var y = rect.height / 2; 1.239 + synthesizeMouse(srcElement, x, y, { type: "mousemove" }, aWindow); 1.240 + synthesizeMouse(srcElement, x+10, y+10, { type: "mousemove" }, aWindow); 1.241 + aWindow.removeEventListener("dragstart", trapDrag, true); 1.242 + 1.243 + event = aWindow.document.createEvent("DragEvents"); 1.244 + event.initDragEvent("dragenter", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); 1.245 + gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true); 1.246 + 1.247 + var event = aWindow.document.createEvent("DragEvents"); 1.248 + event.initDragEvent("dragover", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); 1.249 + if (gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true)) { 1.250 + synthesizeMouseAtCenter(destElement, { type: "mouseup" }, aWindow); 1.251 + return "none"; 1.252 + } 1.253 + 1.254 + if (dataTransfer.dropEffect != "none") { 1.255 + event = aWindow.document.createEvent("DragEvents"); 1.256 + event.initDragEvent("drop", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); 1.257 + gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true); 1.258 + } 1.259 + 1.260 + synthesizeMouseAtCenter(destElement, { type: "mouseup" }, aWindow); 1.261 + 1.262 + return dataTransfer.dropEffect; 1.263 + } finally { 1.264 + ds.endDragSession(true); 1.265 + } 1.266 +}; 1.267 + 1.268 +var PluginUtils = 1.269 +{ 1.270 + withTestPlugin : function(callback) 1.271 + { 1.272 + if (typeof Components == "undefined") 1.273 + { 1.274 + todo(false, "Not a Mozilla-based browser"); 1.275 + return false; 1.276 + } 1.277 + 1.278 + var ph = Components.classes["@mozilla.org/plugin/host;1"] 1.279 + .getService(Components.interfaces.nsIPluginHost); 1.280 + var tags = ph.getPluginTags(); 1.281 + 1.282 + // Find the test plugin 1.283 + for (var i = 0; i < tags.length; i++) 1.284 + { 1.285 + if (tags[i].name == "Test Plug-in") 1.286 + { 1.287 + callback(tags[i]); 1.288 + return true; 1.289 + } 1.290 + } 1.291 + todo(false, "Need a test plugin on this platform"); 1.292 + return false; 1.293 + } 1.294 +};